[clang] 517b22f - [clang] Fix crash on implicit conversion of a reference NTTP bound to a subobject (#215900)

via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 13 15:48:17 PDT 2026


Author: Helmut Januschka
Date: 2026-08-13T15:48:13-07:00
New Revision: 517b22f7f0258e1b3d8ce5ba3ee34066cb64d529

URL: https://github.com/llvm/llvm-project/commit/517b22f7f0258e1b3d8ce5ba3ee34066cb64d529
DIFF: https://github.com/llvm/llvm-project/commit/517b22f7f0258e1b3d8ce5ba3ee34066cb64d529.diff

LOG: [clang] Fix crash on implicit conversion of a reference NTTP bound to a subobject (#215900)

A non-type template parameter of pointer or reference type is substituted
with a ConstantExpr wrapping an OpaqueValueExpr that has no source
expression, see BuildExpressionFromNonTypeTemplateArgumentValue().

When such a parameter appears in a context that needs an implicit
conversion, the -Wconversion machinery inspects the expression, and
TryGetExprRange() recursed into OpaqueValueExpr::getSourceExpr() without
checking for null. The recursive call then dereferenced null in
Expr::IgnoreParens():

  struct S { static bool arr[2]; };
  bool S::arr[2];
  template <bool &Ref> int f() { return Ref; }
  int g() { return f<S::arr[1]>(); }

Only recurse when there is a source expression, matching what
CheckImplicitConversion() already does for OpaqueValueExpr, and otherwise
fall back to the range of the expression's type.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaChecking.cpp
    clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4962f9f137b5a..9a19bb2f2d5c7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -476,6 +476,10 @@ features cannot lower the translation-unit ABI level;
 - Fixed merging of lambdas across modules in the case where neither lambda is
   imported from an AST file. (#GH214560)
 
+- Fixed a crash when a non-type template parameter of reference type is bound
+  to a subobject and is used in a context that requires an implicit conversion.
+  (#GH215900)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index f2f38c84dc5f8..3e6266b8ac542 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12255,9 +12255,14 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
     }
   }
 
-  if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E))
-    return TryGetExprRange(C, OVE->getSourceExpr(), MaxWidth, InConstantContext,
-                           Approximate);
+  if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E)) {
+    // The source expression is null for the OpaqueValueExpr that stands in for
+    // a non-type template argument of pointer or reference type; fall back to
+    // the range of the type in that case.
+    if (const Expr *SourceExpr = OVE->getSourceExpr())
+      return TryGetExprRange(C, SourceExpr, MaxWidth, InConstantContext,
+                             Approximate);
+  }
 
   if (const auto *BitField = E->getSourceBitField())
     return IntRange(BitField->getBitWidthValue(),

diff  --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
index 8450ff037e184..924e27a7155a7 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
@@ -386,3 +386,20 @@ void test() {
     g<X>();
 }
 }
+
+namespace GH215900 {
+// A non-type template parameter of reference type bound to a subobject is
+// represented as a ConstantExpr wrapping a source-less OpaqueValueExpr; the
+// implicit conversion checks used to crash when walking into it.
+struct S {
+  static bool arr[2];
+  bool b;
+};
+bool S::arr[2];
+S s;
+
+template <bool &Ref> int f() { return Ref; }
+template <bool &Ref> int g() { int n = 0; n += Ref; return n; }
+
+int test() { return f<S::arr[1]>() + g<S::arr[0]>() + f<s.b>(); }
+}


        


More information about the cfe-commits mailing list