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 @@ -1068,10 +1068,13 @@ public: SynthesizedFunctionScope(Sema &S, DeclContext *DC) : S(S), SavedContext(S, DC) { + auto *FD = dyn_cast(DC); S.PushFunctionScope(); S.PushExpressionEvaluationContext( - Sema::ExpressionEvaluationContext::PotentiallyEvaluated); - if (auto *FD = dyn_cast(DC)) { + (FD && FD->isConsteval()) + ? ExpressionEvaluationContext::ImmediateFunctionContext + : ExpressionEvaluationContext::PotentiallyEvaluated); + if (FD) { FD->setWillHaveBody(true); S.ExprEvalContexts.back().InImmediateFunctionContext = FD->isImmediateFunction(); @@ -1096,8 +1099,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 +6563,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/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5319,6 +5319,10 @@ const Decl *TargetDecl = OrigCallee.getAbstractInfo().getCalleeDecl().getDecl(); + assert((!isa_and_present(TargetDecl) || + !cast(TargetDecl)->isImmediateFunction()) && + "trying to emit a call to an immediate function"); + CalleeType = getContext().getCanonicalType(CalleeType); auto PointeeType = cast(CalleeType)->getPointeeType(); 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); } @@ -6224,6 +6218,9 @@ if (Field->isInvalidDecl()) return ExprError(); + CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers(), + Field->getParent() != nullptr); + auto *ParentRD = cast(Field->getParent()); std::optional @@ -6271,13 +6268,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 +18408,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 +18438,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); } @@ -18893,6 +18895,12 @@ // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed // context), its initializers may not be referenced yet. if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { + EnterExpressionEvaluationContext EvalContext( + *this, + Constructor->isImmediateFunction() + ? ExpressionEvaluationContext::ImmediateFunctionContext + : ExpressionEvaluationContext::PotentiallyEvaluated, + Constructor); for (CXXCtorInitializer *Init : Constructor->inits()) { if (Init->isInClassMemberInitializer()) runWithSufficientStackSpace(Init->getSourceLocation(), [&]() { diff --git a/clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp b/clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -emit-llvm %s -std=c++2a -triple x86_64-unknown-linux-gnu -o %t.ll +// RUN: FileCheck -input-file=%t.ll %s + +namespace GH63742 { + +void side_effect(); +consteval int f(int x) { + if (!__builtin_is_constant_evaluated()) side_effect(); + return x; +} +struct SS { + int x = f(42); + SS(); +}; +SS::SS(){} + +} + +// CHECK-LABEL: @_ZN7GH637422SSC2Ev +// CHECK-NOT: call +// CHECK: store i32 42, ptr {{.*}} +// CHECK: ret void 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,164 @@ 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()'}} +} +} + +namespace UserDefinedConstructors { +consteval int f(int x) { + return x; +} +extern int NonConst; // expected-note 2{{declared here}} + +struct ConstevalCtr { + int y; + int x = f(y); + consteval ConstevalCtr(int yy) + : y(f(yy)) {} +}; + +ConstevalCtr c1(1); +ConstevalCtr c2(NonConst); +// expected-error@-1 {{call to consteval function 'UserDefinedConstructors::ConstevalCtr::ConstevalCtr' is not a constant expression}} \ +// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}} + +struct ImmediateEscalating { + int y; + int x = f(y); + template + constexpr ImmediateEscalating(T yy) + : y(f(yy)) {} +}; + +ImmediateEscalating c3(1); +ImmediateEscalating c4(NonConst); +// expected-error@-1 {{call to immediate function 'UserDefinedConstructors::ImmediateEscalating::ImmediateEscalating' is not a constant expression}} \ +// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}} + + +struct NonEscalating { + int y; + int x = f(this->y); // expected-error {{call to consteval function 'UserDefinedConstructors::f' is not a constant expression}} \ + // expected-note {{declared here}} \ + // expected-note {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}} + constexpr NonEscalating(int yy) : y(yy) {} // expected-note {{in the default initializer of 'x'}} +}; +NonEscalating s = {1}; + +} + +namespace AggregateInit { + +consteval int f(int x) { + return x; +} + +struct S { + int i; + int j = f(i); +}; + +constexpr S test(auto) { + return {}; +} + +S s = test(0); + +} + +namespace GlobalAggregateInit { + +consteval int f(int x) { + return x; +} + +struct S { + int i; + int j = f(i); // expected-error {{call to consteval function 'GlobalAggregateInit::f' is not a constant expression}} \ + // expected-note {{in the default initializer of 'j'}} \ + // expected-note {{implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}} \ + // expected-note {{declared here}} +}; + +S s(0); +}