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
