[clang] [clang] Handle consteval constructors with default initialization. (PR #144970)
Eli Friedman via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 1 16:56:57 PDT 2025
https://github.com/efriedma-quic updated https://github.com/llvm/llvm-project/pull/144970
>From 75ae08046945b6e8e6bcc5af0e1618393f2cd95d Mon Sep 17 00:00:00 2001
From: Eli Friedman <efriedma at quicinc.com>
Date: Thu, 19 Jun 2025 18:29:49 -0700
Subject: [PATCH] [clang] Consistently handle consteval constructors for
variables.
443377a9d1a8d4a69a317a1a892184c59dd0aec6 handled simple variables
definitions, but it didn't handle uninitialized variables with a
constexpr constructor, and it didn't handle template instantiation.
Fixes #135281 .
---
clang/include/clang/Sema/Sema.h | 1 +
clang/lib/Parse/ParseDecl.cpp | 1 +
clang/lib/Sema/SemaCoroutine.cpp | 3 +-
clang/lib/Sema/SemaDecl.cpp | 6 ++++
clang/lib/Sema/SemaDeclCXX.cpp | 20 ++---------
clang/lib/Sema/SemaExpr.cpp | 19 +++++++++++
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +-
.../SemaCXX/cxx2b-consteval-propagate.cpp | 34 +++++++++++++++++++
8 files changed, 67 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9397546c8fc5d..e335814a910cc 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6759,6 +6759,7 @@ class Sema final : public SemaBase {
EK_Decltype,
EK_TemplateArgument,
EK_AttrArgument,
+ EK_VariableInit,
EK_Other
} ExprContext;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7e739e09b15e8..c34a1fb6004a9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2710,6 +2710,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
break;
}
case InitKind::Uninitialized: {
+ InitializerScopeRAII InitScope(*this, D, ThisDecl);
Actions.ActOnUninitializedDecl(ThisDecl);
break;
}
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index a1389c6c034b1..d193a33f22393 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -783,7 +783,8 @@ static bool checkSuspensionContext(Sema &S, SourceLocation Loc,
const auto ExprContext = S.currentEvaluationContext().ExprContext;
const bool BadContext =
S.isUnevaluatedContext() ||
- ExprContext != Sema::ExpressionEvaluationContextRecord::EK_Other;
+ (ExprContext != Sema::ExpressionEvaluationContextRecord::EK_Other &&
+ ExprContext != Sema::ExpressionEvaluationContextRecord::EK_VariableInit);
if (BadContext) {
S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword;
return false;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e1cccf068b5aa..74530b9fd4246 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14400,6 +14400,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
Var->getType().getAddressSpace() == LangAS::hlsl_input)
return;
+ if (getLangOpts().CPlusPlus)
+ ActOnCXXEnterDeclInitializer(nullptr, Var);
+
// C++03 [dcl.init]p9:
// If no initializer is specified for an object, and the
// object is of (possibly cv-qualified) non-POD class type (or
@@ -14435,6 +14438,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
}
CheckCompleteVariableDeclaration(Var);
+
+ if (getLangOpts().CPlusPlus)
+ ActOnCXXExitDeclInitializer(nullptr, Var);
}
}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 16645ecf411e5..b5e5649305a04 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18937,7 +18937,8 @@ void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) {
EnterDeclaratorContext(S, D->getDeclContext());
PushExpressionEvaluationContext(
- ExpressionEvaluationContext::PotentiallyEvaluated, D);
+ ExpressionEvaluationContext::PotentiallyEvaluated, D,
+ ExpressionEvaluationContextRecord::EK_VariableInit);
}
void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) {
@@ -18946,23 +18947,6 @@ void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) {
if (S && D->isOutOfLine())
ExitDeclaratorContext(S);
- if (getLangOpts().CPlusPlus23) {
- // An expression or conversion is 'manifestly constant-evaluated' if it is:
- // [...]
- // - the initializer of a variable that is usable in constant expressions or
- // has constant initialization.
- if (auto *VD = dyn_cast<VarDecl>(D);
- VD && (VD->isUsableInConstantExpressions(Context) ||
- VD->hasConstantInitialization())) {
- // An expression or conversion is in an 'immediate function context' if it
- // is potentially evaluated and either:
- // [...]
- // - it is a subexpression of a manifestly constant-evaluated expression
- // or conversion.
- ExprEvalContexts.back().InImmediateFunctionContext = true;
- }
- }
-
// Unless the initializer is in an immediate function context (as determined
// above), this will evaluate all contained immediate function calls as
// constant expressions. If the initializer IS an immediate function context,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a3f534ee6712e..d74885491448a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -17935,6 +17935,25 @@ HandleImmediateInvocations(Sema &SemaRef,
Rec.isImmediateFunctionContext() || SemaRef.RebuildingImmediateInvocation)
return;
+ // An expression or conversion is 'manifestly constant-evaluated' if it is:
+ // [...]
+ // - the initializer of a variable that is usable in constant expressions or
+ // has constant initialization.
+ if (SemaRef.getLangOpts().CPlusPlus23 &&
+ Rec.ExprContext ==
+ Sema::ExpressionEvaluationContextRecord::EK_VariableInit) {
+ auto *VD = cast<VarDecl>(Rec.ManglingContextDecl);
+ if (VD->isUsableInConstantExpressions(SemaRef.Context) ||
+ VD->hasConstantInitialization()) {
+ // An expression or conversion is in an 'immediate function context' if it
+ // is potentially evaluated and either:
+ // [...]
+ // - it is a subexpression of a manifestly constant-evaluated expression
+ // or conversion.
+ return;
+ }
+ }
+
/// When we have more than 1 ImmediateInvocationCandidates or previously
/// failed immediate invocations, we need to check for nested
/// ImmediateInvocationCandidates in order to avoid duplicate diagnostics.
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index a25bfd1c48dee..136fae78fd01f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6103,7 +6103,8 @@ void Sema::InstantiateVariableInitializer(
ContextRAII SwitchContext(*this, Var->getDeclContext());
EnterExpressionEvaluationContext Evaluated(
- *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
+ *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var,
+ ExpressionEvaluationContextRecord::EK_VariableInit);
currentEvaluationContext().InLifetimeExtendingContext =
parentEvaluationContext().InLifetimeExtendingContext;
currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
index dd5063cb29c5b..c4cfd9398920a 100644
--- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
+++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
@@ -576,3 +576,37 @@ int f() {
//expected-note at -2 {{read of non-const variable 'a' is not allowed in a constant expression}}
}
}
+
+#if __cplusplus >= 202302L
+namespace GH135281 {
+ struct B {
+ const void* p;
+ consteval B() : p{this} {}
+ };
+ B b;
+ B b2{};
+ B &&b3{};
+ void f() {
+ static B b4;
+ B b5; // expected-error {{call to consteval function 'GH135281::B::B' is not a constant expression}} \
+ // expected-note {{pointer to temporary is not a constant expression}} \
+ // expected-note {{temporary created here}}
+ }
+ template<typename T> T temp_var_uninit;
+ template<typename T> T temp_var_brace_init{};
+ B* b6 = &temp_var_uninit<B>;
+ B* b7 = &temp_var_brace_init<B>;
+ B* b8 = &temp_var_brace_init<B&&>;
+ template<typename T> void f2() {
+ static T b9;
+ T b10; // expected-error {{call to consteval function 'GH135281::B::B' is not a constant expression}} \
+ // expected-note {{pointer to temporary is not a constant expression}} \
+ // expected-note {{temporary created here}}
+ static B b11;
+ B b12; // expected-error 2 {{call to consteval function 'GH135281::B::B' is not a constant expression}} \
+ // expected-note 2 {{pointer to temporary is not a constant expression}} \
+ // expected-note 2 {{temporary created here}}
+ }
+ void (*ff)() = f2<B>; // expected-note {{instantiation of function template specialization}}
+}
+#endif
More information about the cfe-commits
mailing list