Author: Yihan Wang
Date: 2026-09-23T09:06:05+08:00
New Revision: 838c705ee43c97dcb11664f926c8937c224d6987

URL: 
https://github.com/llvm/llvm-project/commit/838c705ee43c97dcb11664f926c8937c224d6987
DIFF: 
https://github.com/llvm/llvm-project/commit/838c705ee43c97dcb11664f926c8937c224d6987.diff

LOG: [clang] Return early if a value dependent recovery init appeared in 
constant evaluation context in legacy constant evaluator (#225027)

A recovery default member initializer can be value-dependent even when
the expression referring to the variable is not. Clang should return
early to avoid crash.

This fix the issue found in
https://github.com/llvm/llvm-project/issues/185874#issuecomment-4058045596.

---------

Signed-off-by: yronglin <[email protected]>

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/AST/ExprConstant.cpp
    clang/test/SemaCXX/recovery-expr-type.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 8e5e5e5732a9b..e5da258b9950a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -679,6 +679,9 @@ features cannot lower the translation-unit ABI level;
   using ``__is_constructible`` on a nested class template inside the definition
   of the containing class. (#GH215166)
 
+- Fixed a crash issue when a value dependent recovery init appeared in constant
+  evaluation context in default constant evaluator.
+
 - Fixed a bug where Clang incorrectly required `promise.return_value()` for a
   dependent `co_return` operand that inits to `void`, instead of using
   `promise.return_void()`. (#GH218368)

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9242491832841..9749d0b43a629 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3520,19 +3520,27 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const 
Expr *E,
   // Used to be C++20 [expr.const]p5.12:
   //  ... reference has a preceding initialization and either ...
   if (Init && Init->isValueDependent()) {
+    if (!Info.checkingPotentialConstantExpression()) {
+      Info.FFDiag(E,
+                  Info.getLangOpts().CPlusPlus11
+                      ? diag::note_constexpr_ltor_non_constexpr
+                      : diag::note_constexpr_ltor_non_integral,
+                  1)
+          << VD << VD->getType();
+      NoteLValueLocation(Info, Base);
+    }
+
+    // A recovery initializer can be value-dependent even when the expression
+    // referring to the variable is not.
+    if (Init->containsErrors())
+      return false;
+
     // The DeclRefExpr is not value-dependent, but the variable it refers to
     // has a value-dependent initializer. This should only happen in
     // constant-folding cases, where the variable is not actually of a suitable
     // type for use in a constant expression (otherwise the DeclRefExpr would
     // have been value-dependent too), so diagnose that.
     assert(!VD->mightBeUsableInConstantExpressions(Info.Ctx));
-    if (!Info.checkingPotentialConstantExpression()) {
-      Info.FFDiag(E, Info.getLangOpts().CPlusPlus11
-                         ? diag::note_constexpr_ltor_non_constexpr
-                         : diag::note_constexpr_ltor_non_integral, 1)
-          << VD << VD->getType();
-      NoteLValueLocation(Info, Base);
-    }
     return false;
   }
 

diff  --git a/clang/test/SemaCXX/recovery-expr-type.cpp 
b/clang/test/SemaCXX/recovery-expr-type.cpp
index bdab2940d6597..101d0155e3b98 100644
--- a/clang/test/SemaCXX/recovery-expr-type.cpp
+++ b/clang/test/SemaCXX/recovery-expr-type.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++17 
-fsyntax-only -verify -fexperimental-new-constant-interpreter
 // RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++20 
-fsyntax-only -verify -fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++17 
-fsyntax-only -verify
-// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++20 
-fsyntax-only -verify
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++17 
-fsyntax-only -verify=expected,ref
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - %s -std=gnu++20 
-fsyntax-only -verify=expected,ref
 
 
 namespace test0 {
@@ -199,3 +199,26 @@ template<int*> struct P;
 S<P> s;
 } // namespace GH202117
 
+namespace test17 {
+struct A { int arr[1]; };
+struct B {
+  static constexpr A &a = A{{0}}; // expected-error {{non-const lvalue 
reference to type 'A' cannot bind to a temporary of type 'A'}}
+};
+
+B x;
+
+int v = x.a.arr[0]; // Do not crash when evaluating a static reference with an 
invalid initializer.
+} // namespace test17
+
+namespace test18 {
+struct B {
+  static constexpr int &a = 0; // expected-error {{non-const lvalue reference 
to type 'int' cannot bind to a temporary of type 'int'}} \
+                                 ref-note {{declared here}}
+};
+
+B x;
+// Diagnose why constant evaluation fails when reading a reference with an
+// invalid initializer.
+static_assert(x.a == 0); // expected-error {{static assertion expression is 
not an integral constant expression}} \
+                           ref-note {{read of non-constexpr variable 'a' is 
not allowed in a constant expression}}
+} // namespace test18


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to