diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1096,8 +1096,10 @@ ~SynthesizedFunctionScope() { if (PushedCodeSynthesisContext) S.popCodeSynthesisContext(); - if (auto *FD = dyn_cast(S.CurContext)) + if (auto *FD = dyn_cast(S.CurContext)) { FD->setWillHaveBody(false); + S.CheckImmediateEscalatingFunctionDefinition(FD, S.getCurFunction()); + } S.PopExpressionEvaluationContext(); S.PopFunctionScopeInfo(); } @@ -6558,8 +6560,9 @@ /// invocation. ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl); - bool CheckImmediateEscalatingFunctionDefinition( - FunctionDecl *FD, bool HasImmediateEscalatingExpression); + bool + CheckImmediateEscalatingFunctionDefinition(FunctionDecl *FD, + const sema::FunctionScopeInfo *); void MarkExpressionAsImmediateEscalating(Expr *E); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15575,8 +15575,7 @@ if (FD) { FD->setBody(Body); FD->setWillHaveBody(false); - CheckImmediateEscalatingFunctionDefinition( - FD, FSI->FoundImmediateEscalatingExpression); + CheckImmediateEscalatingFunctionDefinition(FD, FSI); if (getLangOpts().CPlusPlus14) { if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() && diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2438,13 +2438,12 @@ } bool Sema::CheckImmediateEscalatingFunctionDefinition( - FunctionDecl *FD, bool HasImmediateEscalatingExpression) { - if (!FD->hasBody() || !getLangOpts().CPlusPlus20 || - !FD->isImmediateEscalating()) + FunctionDecl *FD, const sema::FunctionScopeInfo *FSI) { + if (!getLangOpts().CPlusPlus20 || !FD->isImmediateEscalating()) return true; FD->setBodyContainsImmediateEscalatingExpressions( - HasImmediateEscalatingExpression); - if (HasImmediateEscalatingExpression) { + FSI->FoundImmediateEscalatingExpression); + if (FSI->FoundImmediateEscalatingExpression) { auto it = UndefinedButUsed.find(FD->getCanonicalDecl()); if (it != UndefinedButUsed.end()) { Diag(it->second, diag::err_immediate_function_used_before_definition) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6084,17 +6084,11 @@ ImmediateCallVisitor(const ASTContext &Ctx) : Context(Ctx) {} bool HasImmediateCalls = false; - bool IsImmediateInvocation = false; - bool shouldVisitImplicitCode() const { return true; } bool VisitCallExpr(CallExpr *E) { - if (const FunctionDecl *FD = E->getDirectCallee()) { + if (const FunctionDecl *FD = E->getDirectCallee()) HasImmediateCalls |= FD->isImmediateFunction(); - if (FD->isConsteval() && !E->isCXX11ConstantExpr(Context)) - IsImmediateInvocation = true; - } - return RecursiveASTVisitor::VisitStmt(E); } @@ -6271,13 +6265,6 @@ if (!NestedDefaultChecking) V.TraverseDecl(Field); if (V.HasImmediateCalls) { - // C++23 [expr.const]/p15 - // An aggregate initialization is an immediate invocation - // if it evaluates a default member initializer that has a subexpression - // that is an immediate-escalating expression. - ExprEvalContexts.back().InImmediateFunctionContext |= - V.IsImmediateInvocation; - ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = @@ -18418,6 +18405,11 @@ if (!CE.getInt()) EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); for (auto *DR : Rec.ReferenceToConsteval) { + // If the expression is immediate escalating, it is not an error; + // The outer context itself becomes immediate and further errors, + // if any, will be handled by DiagnoseImmediateEscalatingReason + if (DR->isImmediateEscalating()) + continue; const auto *FD = cast(DR->getDecl()); const NamedDecl *ND = FD; if (const auto *MD = dyn_cast(ND); @@ -18443,8 +18435,15 @@ SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) << ND << isa(ND) << FD->isConsteval(); SemaRef.Diag(ND->getLocation(), diag::note_declared_at); + if (auto Context = + SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { + SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) + << Context->Decl; + SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + } if (FD->isImmediateEscalating() && !FD->isConsteval()) SemaRef.DiagnoseImmediateEscalatingReason(FD); + } else { SemaRef.MarkExpressionAsImmediateEscalating(DR); } diff --git a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp --- a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp @@ -41,19 +41,21 @@ }(); }; -consteval int ub(int n) { // expected-note {{declared here}} +consteval int ub(int n) { return 0/n; } struct InitWithLambda { - int b = [](int error = undefined()) { // expected-error {{cannot take address of consteval function 'undefined' outside of an immediate invocation}} + int b = [](int error = undefined()) { // expected-note {{undefined function 'undefined' cannot be used in a constant expression}} return error; }(); - int c = [](int error = sizeof(undefined()) + ub(0)) { // expected-error {{cannot take address of consteval function 'ub' outside of an immediate invocation}} + int c = [](int error = sizeof(undefined()) + ub(0)) { return error; }(); } i; +// expected-error@-1 {{call to immediate function 'InitWithLambda::InitWithLambda' is not a constant expression}} \ +// expected-note@-1 {{in call to 'InitWithLambda()'}} namespace ShouldNotCrash { template diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -164,3 +164,85 @@ int i = g(x); // expected-error {{call to immediate function 'ConstevalConstructor::g' is not a constant expression}} \ // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}} } + + + +namespace Aggregate { +consteval int f(int); // expected-note {{declared here}} +struct S { + int x = f(42); // expected-note {{undefined function 'f' cannot be used in a constant expression}} +}; + +constexpr S immediate(auto) { + return S{}; +} + +void test_runtime() { + (void)immediate(0); // expected-error {{call to immediate function 'Aggregate::immediate' is not a constant expression}} \ + // expected-note {{in call to 'immediate(0)'}} +} +consteval int f(int i) { + return i; +} +consteval void test() { + constexpr S s = immediate(0); + static_assert(s.x == 42); +} +} + + + +namespace GH63742 { +void side_effect(); // expected-note {{declared here}} +consteval int f(int x) { + if (!x) side_effect(); // expected-note {{non-constexpr function 'side_effect' cannot be used in a constant expression}} + return x; +} +struct SS { + int x = f(0); // expected-error {{call to consteval function 'GH63742::f' is not a constant expression}} \ + // expected-note {{declared here}} \ + // expected-note {{in call to 'f(0)'}} + SS(); +}; +SS::SS(){} // expected-note {{in the default initializer of 'x'}} + +consteval int f2(int x) { + if (!__builtin_is_constant_evaluated()) side_effect(); + return x; +} +struct S2 { + int x = f2(0); + constexpr S2(); +}; + +constexpr S2::S2(){} +S2 s = {}; + +struct S3 { + int x = f2(0); + S3(); +}; +S3::S3(){} + +} + +namespace Defaulted { +consteval int f(int x); +struct SS { + int x = f(0); + SS() = default; +}; +} + +namespace DefaultedUse{ +consteval int f(int x); // expected-note {{declared here}} +struct SS { + int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}} + SS() = default; +}; + +void test() { + [[maybe_unused]] SS s; // expected-error {{call to immediate function 'DefaultedUse::SS::SS' is not a constant expression}} \ + // expected-note {{in call to 'SS()'}} +} +}