diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2923,10 +2923,9 @@ Declarator &D, SmallVectorImpl &ParamInfo); void ParseParameterDeclarationClause( - DeclaratorContext DeclaratorContext, - ParsedAttributes &attrs, - SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc); + DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, + SmallVectorImpl &ParamInfo, + SourceLocation &EllipsisLoc, bool InConstantConstext = false); void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); 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 @@ -1141,6 +1141,16 @@ /// Whether we are in a decltype expression. bool IsDecltype; + enum ImmediateInvocationEndScopeAction { + IIESA_Handle, ///< Immediate invocation can and should be handeled at the + ///< end of the scope. + IIESA_Propagate, ///< Immediate invocation cannot be handeled at the end + ///< of the scope and should be propagated to the outer + ///< scope. + IIESA_Drop, ///< Immediate invocation should be dropped at the end of the + ///< scope. + } IIEndScopeAction = IIESA_Handle; + /// The number of active cleanup objects when we entered /// this expression evaluation context. unsigned NumCleanupObjects; @@ -2414,6 +2424,8 @@ bool &AddToScope); bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); + void HandleImmediateInvocations(Sema::ExpressionEvaluationContextRecord &Rec); + enum class CheckConstexprKind { /// Diagnose issues that are non-constant or that are extensions. Diagnose, @@ -4674,7 +4686,8 @@ void PushExpressionEvaluationContext( ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl = nullptr, ExpressionEvaluationContextRecord::ExpressionKind Type = - ExpressionEvaluationContextRecord::EK_Other); + ExpressionEvaluationContextRecord::EK_Other, + bool IsLambdaParamScope = false); enum ReuseLambdaContextDecl_t { ReuseLambdaContextDecl }; void PushExpressionEvaluationContext( ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t, @@ -12620,11 +12633,11 @@ Decl *LambdaContextDecl = nullptr, Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext = Sema::ExpressionEvaluationContextRecord::EK_Other, - bool ShouldEnter = true) + bool ShouldEnter = true, bool IsLambdaParamScope = false) : Actions(Actions), Entered(ShouldEnter) { if (Entered) Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl, - ExprContext); + ExprContext, IsLambdaParamScope); } EnterExpressionEvaluationContext( Sema &Actions, Sema::ExpressionEvaluationContext NewContext, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6455,8 +6455,9 @@ ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, - EllipsisLoc); + ParseParameterDeclarationClause( + D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc, + D.getDeclSpec().getConstexprSpecifier() == CSK_consteval); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6705,10 +6706,9 @@ /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - DeclaratorContext DeclaratorCtx, - ParsedAttributes &FirstArgAttrs, - SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc) { + DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, + SmallVectorImpl &ParamInfo, + SourceLocation &EllipsisLoc, bool InConstantConstext) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -6859,8 +6859,13 @@ // used. EnterExpressionEvaluationContext Eval( Actions, - Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed, - Param); + InConstantConstext + ? Sema::ExpressionEvaluationContext::ConstantEvaluated + : Sema::ExpressionEvaluationContext:: + PotentiallyEvaluatedIfUsed, + Param, Sema::ExpressionEvaluationContextRecord::EK_Other, + /*ShouldEnter*/ true, + DeclaratorCtx == DeclaratorContext::LambdaExprContext); ExprResult DefArgResult; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1023,6 +1023,8 @@ DiagnoseUnterminatedPragmaPack(); DiagnoseUnterminatedPragmaAttribute(); + HandleImmediateInvocations(ExprEvalContexts.back()); + // All delayed member exception specs should be checked or we end up accepting // incompatible declarations. assert(DelayedOverridingExceptionSpecChecks.empty()); 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 @@ -17041,6 +17041,11 @@ if (isNonlocalVariable(D)) PushExpressionEvaluationContext( ExpressionEvaluationContext::PotentiallyEvaluated, D); + + bool HasConstantInitializer = false; + if (auto *VD = dyn_cast(D)) + HasConstantInitializer = VD->isConstexpr() || VD->hasAttr(); + isConstantEvaluatedOverride = HasConstantInitializer; } /// Invoked after we are finished parsing an initializer for the declaration D. @@ -17054,6 +17059,7 @@ if (S && D->isOutOfLine()) ExitDeclaratorContext(S); + isConstantEvaluatedOverride = false; } /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a 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 @@ -16097,13 +16097,16 @@ return TransformToPE(*this).TransformExpr(E); } -void -Sema::PushExpressionEvaluationContext( +void Sema::PushExpressionEvaluationContext( ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, - ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { + ExpressionEvaluationContextRecord::ExpressionKind ExprContext, + bool IsLambdaParamScope) { ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, LambdaContextDecl, ExprContext); Cleanup.reset(); + if (IsLambdaParamScope) + ExprEvalContexts.back().IIEndScopeAction = + Sema::ExpressionEvaluationContextRecord::IIESA_Propagate; if (!MaybeODRUseExprs.empty()) std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); } @@ -16183,7 +16186,9 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() || - RebuildingImmediateInvocation) + RebuildingImmediateInvocation || + ExprEvalContexts.back().IIEndScopeAction == + ExpressionEvaluationContextRecord::IIESA_Drop) return E; /// Opportunistically remove the callee from ReferencesToConsteval if we can. @@ -16312,14 +16317,31 @@ It->getPointer()->setSubExpr(Res.get()); } -static void -HandleImmediateInvocations(Sema &SemaRef, - Sema::ExpressionEvaluationContextRecord &Rec) { +void Sema::HandleImmediateInvocations( + Sema::ExpressionEvaluationContextRecord &Rec) { if ((Rec.ImmediateInvocationCandidates.size() == 0 && Rec.ReferenceToConsteval.size() == 0) || - SemaRef.RebuildingImmediateInvocation) + RebuildingImmediateInvocation || + Rec.IIEndScopeAction == + Sema::ExpressionEvaluationContextRecord::IIESA_Drop) return; + /// If we can't handle immediate invocations yet. add them to the outer scope. + /// This occurs for default argument of lambdas as we can't know if the lambda + /// is consteval until after the parameter context has been poped. + if (Rec.IIEndScopeAction == + Sema::ExpressionEvaluationContextRecord::IIESA_Propagate) { + assert(ExprEvalContexts.size() >= 2); + (ExprEvalContexts.rbegin() + 1) + ->ImmediateInvocationCandidates.append( + Rec.ImmediateInvocationCandidates.begin(), + Rec.ImmediateInvocationCandidates.end()); + (ExprEvalContexts.rbegin() + 1) + ->ReferenceToConsteval.insert(Rec.ReferenceToConsteval.begin(), + Rec.ReferenceToConsteval.end()); + return; + } + /// When we have more then 1 ImmediateInvocationCandidates we need to check /// for nested ImmediateInvocationCandidates. when we have only 1 we only /// need to remove ReferenceToConsteval in the immediate invocation. @@ -16327,16 +16349,16 @@ /// Prevent sema calls during the tree transform from adding pointers that /// are already in the sets. - llvm::SaveAndRestore DisableIITracking( - SemaRef.RebuildingImmediateInvocation, true); + llvm::SaveAndRestore DisableIITracking(RebuildingImmediateInvocation, + true); /// Prevent diagnostic during tree transfrom as they are duplicates - Sema::TentativeAnalysisScope DisableDiag(SemaRef); + Sema::TentativeAnalysisScope DisableDiag(*this); for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); It != Rec.ImmediateInvocationCandidates.rend(); It++) if (!It->getInt()) - RemoveNestedImmediateInvocation(SemaRef, Rec, It); + RemoveNestedImmediateInvocation(*this, Rec, It); } else if (Rec.ImmediateInvocationCandidates.size() == 1 && Rec.ReferenceToConsteval.size()) { struct SimpleRemove : RecursiveASTVisitor { @@ -16352,12 +16374,11 @@ } for (auto CE : Rec.ImmediateInvocationCandidates) if (!CE.getInt()) - EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + EvaluateAndDiagnoseImmediateInvocation(*this, CE); for (auto DR : Rec.ReferenceToConsteval) { auto *FD = cast(DR->getDecl()); - SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) - << FD; - SemaRef.Diag(FD->getLocation(), diag::note_declared_at); + Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) << FD; + Diag(FD->getLocation(), diag::note_declared_at); } } @@ -16394,7 +16415,7 @@ } WarnOnPendingNoDerefs(Rec); - HandleImmediateInvocations(*this, Rec); + HandleImmediateInvocations(Rec); // Warn on any volatile-qualified simple-assignments that are not discarded- // value expressions nor unevaluated operands (those cases get removed from diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -891,6 +891,10 @@ LambdaScopeInfo *const LSI = getCurLambda(); assert(LSI && "LambdaScopeInfo should be on stack!"); + if (ParamInfo.getDeclSpec().getConstexprSpecifier() == CSK_consteval) + ExprEvalContexts.back().IIEndScopeAction = + ExpressionEvaluationContextRecord::IIESA_Drop; + // Determine if we're within a context where we know that the lambda will // be dependent, because there are template parameters in scope. bool KnownDependent; diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -258,6 +258,26 @@ return f(0); }; +consteval int f1() { +// expected-note@-1+ {{declared here}} + return 0; +} +consteval auto g() { return f1; } +consteval int h(int (*p)() = g()) { return p(); } +int h1(int (*p)() = g()) { return p(); } +// expected-error@-1 {{is not a constant expression}} +// expected-note@-2 {{pointer to a consteval}} + +constexpr auto e = g(); +// expected-error@-1 {{must be initialized by a constant expression}} +// expected-note@-2 {{is not a constant expression}} + +auto l = [](int (*p)() = g()) { return p(); }; +// expected-error@-1 {{is not a constant expression}} +// expected-note@-2 {{pointer to a consteval}} + +auto l2 = [](int (*p)() = g()) consteval { return p(); }; + } namespace std { @@ -594,3 +614,22 @@ } } // namespace special_ctor + +namespace top_level { +struct S { + consteval S() {} + int a; +// expected-note@-1 {{subobject declared here}} +}; + +S s; // expected-error {{is not a constant expression}} +// expected-note@-1 {{is not initialized}} + +struct S1 { + consteval S1() {} + int a = 0; +}; + +S1 s1; + +}