diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1066,6 +1066,8 @@ return SubExpr->getEndLoc(); } + void markFailed() { setDependence(ExprDependence::Error); } + static bool classof(const Stmt *T) { return T->getStmtClass() == ConstantExprClass; } 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 InConstantContext = false); void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -881,6 +881,14 @@ SourceLocation PotentialThisCaptureLocation; + /// Set of candidates for starting an immediate invocation. + llvm::SmallVector, 2> + ImmediateInvocationCandidates; + + /// Set of DeclRefExprs referencing a consteval function when used in a + /// context not already known to be immediately invoked. + llvm::SmallPtrSet ReferenceToConsteval; + LambdaScopeInfo(DiagnosticsEngine &Diag) : CapturingScopeInfo(Diag, ImpCap_None) { Kind = SK_Lambda; 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 @@ -1185,7 +1185,10 @@ /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { - EK_Decltype, EK_TemplateArgument, EK_Other + EK_Decltype, + EK_TemplateArgument, + EK_LambdaParam, + EK_Other } ExprContext; ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, @@ -2414,6 +2417,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, 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 InConstantContext) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -6859,8 +6859,14 @@ // used. EnterExpressionEvaluationContext Eval( Actions, - Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed, - Param); + InConstantContext + ? Sema::ExpressionEvaluationContext::ConstantEvaluated + : Sema::ExpressionEvaluationContext:: + PotentiallyEvaluatedIfUsed, + Param, + DeclaratorCtx == DeclaratorContext::LambdaExprContext + ? Sema::ExpressionEvaluationContextRecord::EK_LambdaParam + : Sema::ExpressionEvaluationContextRecord::EK_Other); 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/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16183,7 +16183,7 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() || - RebuildingImmediateInvocation) + RebuildingImmediateInvocation || isUnevaluatedContext()) return E; /// Opportunistically remove the callee from ReferencesToConsteval if we can. @@ -16229,6 +16229,7 @@ SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) << FD; for (auto &Note : Notes) SemaRef.Diag(Note.first, Note.second); + CE->markFailed(); return; } CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); @@ -16312,13 +16313,25 @@ 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 || isUnevaluatedContext()) + return; + + /// If the scope is lambda params we don't know wether the lambda is consteval + /// yet. So we store all the needed information in the lambda and resolve it + /// later. + if (Rec.ExprContext == + Sema::ExpressionEvaluationContextRecord::EK_LambdaParam) { + getCurLambda()->ImmediateInvocationCandidates.append( + Rec.ImmediateInvocationCandidates.begin(), + Rec.ImmediateInvocationCandidates.end()); + getCurLambda()->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 @@ -16327,16 +16340,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 { @@ -16351,13 +16364,12 @@ Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); } for (auto CE : Rec.ImmediateInvocationCandidates) - if (!CE.getInt()) - EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + if (!CE.getInt() && !CE.getPointer()->hasAPValueResult()) + 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 +16406,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,16 @@ LambdaScopeInfo *const LSI = getCurLambda(); assert(LSI && "LambdaScopeInfo should be on stack!"); + /// If the lambda is consteval the paramters will be evaluated in constant + /// context immediate invocations don't need to be kept. + if (ParamInfo.getDeclSpec().getConstexprSpecifier() != CSK_consteval) { + ExprEvalContexts.back().ReferenceToConsteval.insert( + LSI->ReferenceToConsteval.begin(), LSI->ReferenceToConsteval.end()); + ExprEvalContexts.back().ImmediateInvocationCandidates.append( + LSI->ImmediateInvocationCandidates.begin(), + LSI->ImmediateInvocationCandidates.end()); + } + // 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 {{call to consteval function}} +// 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,42 @@ } } // 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; + +} + +namespace unevaluated { +struct N { + constexpr N() {} + N(N const&) = delete; +}; +template constexpr void bad_assert_copyable() { T t; T t2 = t; } +using ineffective = decltype(bad_assert_copyable()); + +template consteval void assert_copyable() { T t; T t2 = t; } +using check = decltype(assert_copyable()); +} + +// namespace error { + +// consteval void check(bool b) { if (!b) throw; } +// constinit int x = true ? 1 : (check(false), 2); +// constinit int x1 = (check(false), 2); + +// }