diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 95b04fe3dd59b..604875cd6337a 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2727,7 +2727,11 @@ DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { TRY_TO(TraverseStmt(S->getExpr())); }) -DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {}) +DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { + if (getDerived().shouldVisitImplicitCode()) + TRY_TO(TraverseStmt(S->getExpr())); +}) + DEF_TRAVERSE_STMT(CXXDeleteExpr, {}) DEF_TRAVERSE_STMT(ExprWithCleanups, {}) DEF_TRAVERSE_STMT(CXXInheritedCtorInitExpr, {}) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b87414d91c238..fe4c3005c0ba3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2685,7 +2685,8 @@ def err_immediate_function_used_before_definition : Error< "immediate function %0 used before it is defined">; def note_immediate_function_reason : Note< - "%0 is an immediate function because its body " + "%0 is an immediate %select{function|constructor}5 because " + "%select{its body|the%select{| default}7 initializer of %8}6 " "%select{evaluates the address of %select{an immediate|a consteval}2 " "function %1|contains a call to %select{an immediate|a consteval}2 " "%select{function|constructor}4 %1 and that call is not a constant " diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 082fa252feaba..3418a37b30778 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1078,10 +1078,13 @@ class Sema final { 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(); @@ -1106,8 +1109,10 @@ class Sema final { ~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(); } @@ -6578,11 +6583,11 @@ class Sema final { ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl); bool CheckImmediateEscalatingFunctionDefinition( - FunctionDecl *FD, bool HasImmediateEscalatingExpression); + FunctionDecl *FD, const sema::FunctionScopeInfo *FSI); void MarkExpressionAsImmediateEscalating(Expr *E); - void DiagnoseImmediateEscalatingReason(const clang::FunctionDecl *FD); + void DiagnoseImmediateEscalatingReason(FunctionDecl *FD); bool CompleteConstructorCall(CXXConstructorDecl *Constructor, QualType DeclInitType, MultiExprArg ArgsPtr, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index a585abf15f829..ed6095f7cfeb0 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5319,6 +5319,10 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee 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/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index e2beb77d196fa..07a9dec12f6f2 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4438,13 +4438,10 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction( /// GetAddrOfFunction - Return the address of the given function. If Ty is /// non-null, then this function will use the specified type if it has to /// create it (this occurs when we see a definition of the function). -llvm::Constant *CodeGenModule::GetAddrOfFunction(GlobalDecl GD, - llvm::Type *Ty, - bool ForVTable, - bool DontDefer, - ForDefinition_t IsForDefinition) { - assert(!cast(GD.getDecl())->isImmediateFunction() && - "an immediate function should never be emitted"); +llvm::Constant * +CodeGenModule::GetAddrOfFunction(GlobalDecl GD, llvm::Type *Ty, bool ForVTable, + bool DontDefer, + ForDefinition_t IsForDefinition) { // If there was no specific requested type, just convert it now. if (!Ty) { const auto *FD = cast(GD.getDecl()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8db77f8558083..a4bf579284709 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15575,8 +15575,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, 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 index 40fbaa043c1bf..c59ab8b69594e 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -46,6 +46,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/SaveAndRestore.h" #include #include #include @@ -2438,13 +2439,12 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl, } 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) @@ -2458,65 +2458,92 @@ bool Sema::CheckImmediateEscalatingFunctionDefinition( return true; } -void Sema::DiagnoseImmediateEscalatingReason(const FunctionDecl *FD) { +void Sema::DiagnoseImmediateEscalatingReason(FunctionDecl *FD) { assert(FD->isImmediateEscalating() && !FD->isConsteval() && "expected an immediate function"); assert(FD->hasBody() && "expected the function to have a body"); struct ImmediateEscalatingExpressionsVisitor : public RecursiveASTVisitor { + using Base = RecursiveASTVisitor; Sema &SemaRef; - const FunctionDecl *FD; - ImmediateEscalatingExpressionsVisitor(Sema &SemaRef, const FunctionDecl *FD) - : SemaRef(SemaRef), FD(FD) {} + + const FunctionDecl *ImmediateFn; + bool ImmediateFnIsConstructor; + CXXConstructorDecl *CurrentConstructor = nullptr; + CXXCtorInitializer *CurrentInit = nullptr; + + ImmediateEscalatingExpressionsVisitor(Sema &SemaRef, FunctionDecl *FD) + : SemaRef(SemaRef), ImmediateFn(FD), + ImmediateFnIsConstructor(isa(FD)) {} bool shouldVisitImplicitCode() const { return true; } bool shouldVisitLambdaBody() const { return false; } + void Diag(const Expr *E, const FunctionDecl *Fn, bool IsCall) { + SourceLocation Loc = E->getBeginLoc(); + SourceRange Range = E->getSourceRange(); + if (CurrentConstructor && CurrentInit) { + Loc = CurrentConstructor->getLocation(); + Range = CurrentInit->isWritten() ? CurrentInit->getSourceRange() + : SourceRange(); + } + SemaRef.Diag(Loc, diag::note_immediate_function_reason) + << ImmediateFn << Fn << Fn->isConsteval() << IsCall + << isa(Fn) << ImmediateFnIsConstructor + << (CurrentInit != nullptr) + << (CurrentInit && !CurrentInit->isWritten()) + << (CurrentInit ? CurrentInit->getAnyMember() : nullptr) << Range; + } bool TraverseCallExpr(CallExpr *E) { if (const auto *DR = dyn_cast(E->getCallee()->IgnoreImplicit()); DR && DR->isImmediateEscalating()) { - SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason) - << FD << E->getDirectCallee() << E->getDirectCallee()->isConsteval() - << /*Call*/ 1 << /*Function*/ 0 << E->getSourceRange(); - } - for (auto A : E->arguments()) { - getDerived().TraverseStmt(A); + Diag(E, E->getDirectCallee(), /*IsCall=*/true); + return false; } + + for (Expr *A : E->arguments()) + if (!getDerived().TraverseStmt(A)) + return false; + return true; } + bool VisitDeclRefExpr(DeclRefExpr *E) { if (const auto *ReferencedFn = dyn_cast(E->getDecl()); ReferencedFn && E->isImmediateEscalating()) { - SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason) - << FD << ReferencedFn << ReferencedFn->isConsteval() - << /*Address*/ 0 << /*Function*/ 0 << E->getSourceRange(); + Diag(E, ReferencedFn, /*IsCall=*/false); + return false; } + return true; } + bool VisitCXXConstructExpr(CXXConstructExpr *E) { CXXConstructorDecl *D = E->getConstructor(); if (E->isImmediateEscalating()) { - SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason) - << FD << D << D->isConsteval() << /*Call*/ 1 << /*Constructor*/ 1 - << E->getSourceRange(); + Diag(E, D, /*IsCall=*/true); + return false; } return true; } - // These nodes can never contain an immediate escalating expression, - // we can skip them to avoid unecessary work. - bool TraverseDecl(Decl *D) { - if (isa(D)) - return true; - return Base::TraverseDecl(D); + bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { + llvm::SaveAndRestore RAII(CurrentInit, Init); + return Base::TraverseConstructorInitializer(Init); + } + + bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctr) { + llvm::SaveAndRestore RAII(CurrentConstructor, Ctr); + return Base::TraverseCXXConstructorDecl(Ctr); } + bool TraverseType(QualType T) { return true; } bool VisitBlockExpr(BlockExpr *T) { return true; } } Visitor(*this, FD); - Visitor.TraverseStmt(FD->getBody()); + Visitor.TraverseDecl(FD); } /// Get the class that is directly named by the current context. This is the diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 716383412cc58..2716b66771059 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6084,17 +6084,11 @@ struct ImmediateCallVisitor : public RecursiveASTVisitor { 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,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { if (Field->isInvalidDecl()) return ExprError(); + CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers()); + auto *ParentRD = cast(Field->getParent()); std::optional @@ -6271,13 +6267,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { 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 = @@ -18445,7 +18434,12 @@ HandleImmediateInvocations(Sema &SemaRef, if (!CE.getInt()) EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); for (auto *DR : Rec.ReferenceToConsteval) { - const auto *FD = cast(DR->getDecl()); + // 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; + auto *FD = cast(DR->getDecl()); const NamedDecl *ND = FD; if (const auto *MD = dyn_cast(ND); MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD))) @@ -18470,8 +18464,15 @@ HandleImmediateInvocations(Sema &SemaRef, 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); } @@ -18920,6 +18921,12 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, // 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-propagate.cpp b/clang/test/CodeGenCXX/cxx2c-consteval-propagate.cpp new file mode 100644 index 0000000000000..df5191b29f750 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-consteval-propagate.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -emit-llvm %s -std=c++20 -triple x86_64-unknown-linux-gnu -o - | FileCheck %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 index da8315a28bbca..be8f7cc788589 100644 --- a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp @@ -41,19 +41,21 @@ struct UnusedInitWithLambda { }(); }; -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}} +struct InitWithLambda { // expected-note {{'InitWithLambda' is an immediate constructor because the default initializer of 'b' contains a call to a consteval function 'undefined' and that call is not a constant expression}} + 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 index 5e149dd6ec5a6..333b98d30b75e 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++2a -emit-llvm-only -Wno-unused-value %s -verify -// RUN: %clang_cc1 -std=c++2b -emit-llvm-only -Wno-unused-value %s -verify +// RUN: %clang_cc1 -std=c++2a -Wno-unused-value %s -verify +// RUN: %clang_cc1 -std=c++2b -Wno-unused-value %s -verify consteval int id(int i) { return i; } constexpr char id(char c) { return c; } @@ -34,7 +34,7 @@ int x = 0; // expected-note {{declared here}} template constexpr T h(T t = id(x)) { // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}} \ - // expected-note 2{{'hh' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}} + // expected-note {{'hh' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}} return t; } @@ -164,3 +164,169 @@ constexpr int g(auto t) { 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}} \ + // expected-note {{'immediate' is an immediate function because its body contains a call to a consteval function 'f' and that call is not 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 y = f(1); // Ok + 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 = {}; +constinit S2 s2 = {}; + +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 a = sizeof(f(0)); // Ok + int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}} + + SS() = default; // expected-note {{'SS' is an immediate constructor because the default initializer of 'x' contains a call to a consteval function 'f' and that call is not a constant expression}} +}; + +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) // expected-note {{ImmediateEscalating' is an immediate constructor because the initializer of 'y' contains a call to a consteval function 'f' and that call is not a constant expression}} + : 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 {{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); // expected-note {{in the default initializer of 'j'}} + +}