Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2310,6 +2310,15 @@ "'constexpr' non-static member function will not be implicitly 'const' " "in C++14; add 'const' to avoid a change in behavior">, InGroup>; +def err_invalid_consteval_take_address : Error< + "cannot take address of consteval function %0 in non-constexpr context">; +def err_consteval_address_accessible : Error< + "%select{return value|constructed object}0 contain pointer on consteval declaration;" + " this would make it accessible at runtime">; +def err_invalid_consteval_call : Error< + "call to consteval %select{function|constructor of}1 %0 could not be evaluated">; +def err_invalid_consteval_decl_kind : Error< + "operator %select{new|delete|new[]|delete[]}0 cannot be consteval specified">; def err_invalid_constexpr : Error< "%select{function parameter|typedef|non-static data member}0 " "cannot be %select{constexpr|consteval}1">; @@ -2395,7 +2404,7 @@ "variables defined in a constexpr %select{function|constructor}0 must be " "initialized">; def ext_constexpr_function_never_constant_expr : ExtWarn< - "constexpr %select{function|constructor}0 never produces a " + "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a " "constant expression">, InGroup>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; @@ -2433,7 +2442,7 @@ def err_constexpr_union_ctor_no_init : Error< "constexpr union constructor does not initialize any member">; def err_constexpr_ctor_missing_init : Error< - "constexpr constructor must initialize all members">; + "%select{constexpr|consteval}0 constructor must initialize all members">; def note_constexpr_ctor_missing_init : Note< "member not initialized by constructor">; def note_non_literal_no_constexpr_ctors : Note< @@ -3731,6 +3740,8 @@ "non-tautological enable_if conditions">; def note_addrof_ovl_candidate_disabled_by_enable_if_attr : Note< "candidate function made ineligible by enable_if">; +def note_addrof_ovl_consteval : Note< + "candidate function made ineligible by consteval specifier">; def note_ovl_candidate_deduced_mismatch : Note< "candidate template ignored: deduced type " "%diff{$ of %select{|element of }4%ordinal0 parameter does not match " Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2117,6 +2117,8 @@ void ActOnUninitializedDecl(Decl *dcl); void ActOnInitializerError(Decl *Dcl); + ExprResult ActOnConstevalCall(FunctionDecl *FD, Expr *Call); + void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc); void ActOnCXXForRangeDecl(Decl *D); StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8640,6 +8640,22 @@ if (isa(NewFD)) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) << (ConstexprKind == CSK_consteval); + // C++20 [dcl.constexpr]p2: A destructor, an allocation function, or a + // deallocation function shall not be declared with the consteval + // specifier. + if (ConstexprKind == CSK_consteval) { + if (CXXMethodDecl *MD = dyn_cast(NewFD)) { + if (MD->getOverloadedOperator() == OO_New || + MD->getOverloadedOperator() == OO_Array_New || + MD->getOverloadedOperator() == OO_Delete || + MD->getOverloadedOperator() == OO_Array_Delete) { + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_invalid_consteval_decl_kind) + << MD->getOverloadedOperator() - OO_New; + NewFD->setConstexprKind(CSK_constexpr); + } + } + } } // If __module_private__ was specified, mark the function accordingly. @@ -13003,7 +13019,9 @@ // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). if (!isLambdaCallOperator(FD)) - PushExpressionEvaluationContext(ExprEvalContexts.back().Context); + PushExpressionEvaluationContext( + FD->isConsteval() ? ExpressionEvaluationContext::ConstantEvaluated + : ExprEvalContexts.back().Context); // Check for defining attributes before the check for redefinition. if (const auto *Attr = FD->getAttr()) { Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -1815,7 +1815,8 @@ if (!Inits.count(Field)) { if (!Diagnosed) { - SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init); + SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init) + << Dcl->isConsteval(); Diagnosed = true; } SemaRef.Diag(Field->getLocation(), diag::note_constexpr_ctor_missing_init); @@ -2111,7 +2112,7 @@ SmallVector Diags; if (!Expr::isPotentialConstantExpr(Dcl, Diags)) { Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr) - << isa(Dcl); + << isa(Dcl) << Dcl->isConsteval(); for (size_t I = 0, N = Diags.size(); I != N; ++I) Diag(Diags[I].first, Diags[I].second); // Don't return false here: we allow this for compatibility in @@ -6704,7 +6705,9 @@ // If a function is explicitly defaulted on its first declaration, it is // implicitly considered to be constexpr if the implicit declaration // would be. - MD->setConstexprKind(Constexpr ? CSK_constexpr : CSK_unspecified); + MD->setConstexprKind( + Constexpr ? (MD->isConsteval() ? CSK_consteval : CSK_constexpr) + : CSK_unspecified); if (!Type->hasExceptionSpec()) { // C++2a [except.spec]p3: @@ -13066,12 +13069,17 @@ if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor)) return ExprError(); - return CXXConstructExpr::Create( - Context, DeclInitType, ConstructLoc, Constructor, Elidable, - ExprArgs, HadMultipleCandidates, IsListInitialization, - IsStdInitListInitialization, RequiresZeroInit, + ExprResult CtorExpr = CXXConstructExpr::Create( + Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs, + HadMultipleCandidates, IsListInitialization, IsStdInitListInitialization, + RequiresZeroInit, static_cast(ConstructKind), ParenRange); + + if (Constructor->isConsteval()) + CtorExpr = ActOnConstevalCall(Constructor, CtorExpr.get()); + + return CtorExpr; } ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -5361,7 +5361,8 @@ /// TODO: Handle pointer return types. static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, const FunctionDecl *FDecl, - MultiExprArg ArgExprs) { + MultiExprArg ArgExprs, + bool Diagnose = true) { QualType DeclType = FDecl->getType(); const FunctionProtoType *FT = dyn_cast(DeclType); @@ -5378,7 +5379,7 @@ // Convert array arguments to pointer to simplify type lookup. ExprResult ArgRes = - Sema->DefaultFunctionArrayLvalueConversion(ArgExprs[i++]); + Sema->DefaultFunctionArrayLvalueConversion(ArgExprs[i++], Diagnose); if (ArgRes.isInvalid()) return nullptr; Expr *Arg = ArgRes.get(); @@ -5539,6 +5540,66 @@ } } +/// Traverses throught an APValue to verify there is no pointers on consteval +/// functions. +static bool CheckPointerOnConsteval(const APValue& Value) { + switch (Value.getKind()){ + default: + return false; + case APValue::Union: + return CheckPointerOnConsteval(Value.getUnionValue()); + case APValue::Struct: + for (unsigned I = 0; I < Value.getStructNumBases(); I++) + if (CheckPointerOnConsteval(Value.getStructBase(I))) + return true; + for (unsigned I = 0; I < Value.getStructNumFields(); I++) + if (CheckPointerOnConsteval(Value.getStructField(I))) + return true; + return false; + case APValue::Array: + for (unsigned I = 0; I < Value.getArrayInitializedElts(); I++) + if (CheckPointerOnConsteval(Value.getArrayInitializedElt(I))) + return true; + return false; + case APValue::Vector: + for (unsigned I = 0; I < Value.getVectorLength(); I++) + if (CheckPointerOnConsteval(Value.getVectorElt(I))) + return true; + return false; + case APValue::MemberPointer: + if (auto* FD = dyn_cast(Value.getMemberPointerDecl())) + return FD->isConsteval(); + return false; + case APValue::LValue: + if (Value.getLValueBase()) + if (auto* VD = Value.getLValueBase().dyn_cast()) + if (auto* FD = dyn_cast(VD)) + return FD->isConsteval(); + return false; + } +} + +ExprResult Sema::ActOnConstevalCall(FunctionDecl *FD, Expr *Call) { + Expr::EvalResult Result; + llvm::SmallVector Diags; + Result.Diag = &Diags; + Call->EvaluateAsConstantExpr(Result, Expr::EvaluateForCodeGen, Context); + if (!Result.Diag->empty()) { + Diag(Call->getBeginLoc(), diag::err_invalid_consteval_call) + << FD << isa(FD); + Diag(FD->getLocation(), diag::note_callee_decl) << FD; + for (auto Note : *Result.Diag) + Diag(Note.first, Note.second); + return ExprError(); + } + if (CheckPointerOnConsteval(Result.Val)) { + Diag(Call->getBeginLoc(), diag::err_consteval_address_accessible) + << isa(FD); + return ExprError(); + } + return ConstantExpr::Create(Context, Call, std::move(Result.Val)); +} + ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { @@ -5559,6 +5620,10 @@ } } + if (CallExpr *CExpr = dyn_cast(Call.get())) + if (auto *FD = dyn_cast_or_null(CExpr->getCalleeDecl())) + if (FD && FD->isConsteval() && !isConstantEvaluated()) + Call = ActOnConstevalCall(FD, CExpr); return Call; } @@ -5676,8 +5741,8 @@ // Rewrite the function decl for this builtin by replacing parameters // with no explicit address space with the address space of the arguments // in ArgExprs. - if ((FDecl = - rewriteBuiltinFunctionDecl(this, Context, FDecl, ArgExprs))) { + if ((FDecl = rewriteBuiltinFunctionDecl(this, Context, FDecl, ArgExprs, + /*Diagnose*/ false))) { NDecl = FDecl; Fn = DeclRefExpr::Create( Context, FDecl->getQualifierLoc(), SourceLocation(), FDecl, false, Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1192,7 +1192,9 @@ // Enter a new evaluation context to insulate the lambda from any // cleanups from the enclosing full-expression. PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated); + LSI->CallOperator->isConsteval() + ? ExpressionEvaluationContext::ConstantEvaluated + : ExpressionEvaluationContext::PotentiallyEvaluated); } void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope, Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -9592,6 +9592,18 @@ bool Complain, bool InOverloadResolution, SourceLocation Loc) { + if (!S.isConstantEvaluated() && FD->isConsteval()) { + if (Complain) { + if (InOverloadResolution) + S.Diag(FD->getBeginLoc(), diag::note_addrof_ovl_consteval); + else { + S.Diag(Loc, diag::err_invalid_consteval_take_address) << FD; + S.Diag(FD->getBeginLoc(), diag::note_declared_at); + } + } + return false; + } + if (!isFunctionAlwaysEnabled(S.Context, FD)) { if (Complain) { if (InOverloadResolution) Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -56,3 +56,276 @@ consteval int main() { // expected-error {{'main' is not allowed to be declared consteval}} return 0; } + +int i_runtime; // expected-note+ {{declared here}} +constexpr int i_constexpr = 0; + +consteval int f_eval(int i) { +// expected-note@-1+ {{declared here}} + return i; +} + +constexpr auto l_eval = [](int i) consteval { +// expected-note@-1+ {{declared here}} + return i; +}; + +struct A { + int I = 0; + consteval int f_eval(int i) const { +// expected-note@-1+ {{declared here}} + return I + i; +// expected-note@-1 {{is not allowed in a constant expression}} + } +}; + +constexpr A a; + +namespace invalid_call { + +int d2 = f_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} +int l2 = l_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} +int m2 = a.f_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} +int d4 = f_eval(i_constexpr); +int l4 = l_eval(i_constexpr); +int m4 = a.f_eval(i_constexpr); + +constexpr int f1(int i) { // expected-note+ {{declared here}} + int d0 = f_eval(0); + int l0 = l_eval(0); + int m0 = a.f_eval(0); + int d2 = f_eval(i); + // expected-error@-1 {{could not be evaluated}} + // expected-error@-2 {{must be initialized}} + // FIXME: the error above should not appear when the initializer present but is invalid. + // expected-note@-4 {{is not allowed in a constant expression}} + + int l2 = l_eval(i); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} + int m2 = a.f_eval(i); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} + int d6 = f_eval(i_constexpr); + int l6 = l_eval(i_constexpr); + int m6 = a.f_eval(i_constexpr); + int d8 = f_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} + int l8 = l_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} + int m8 = a.f_eval(i_runtime); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{is not allowed in a constant expression}} + return 0; +} + +consteval int f2(int i) { +// expected-error@-1 {{never produces a constant expression}} + int d0 = f_eval(i); + int l0 = l_eval(i); + int m0 = a.f_eval(i); + int d1 = f_eval(i_runtime); +// expected-note@-1 {{is not allowed in a constant expression}} + int l1 = l_eval(i_runtime); + int m1 = a.f_eval(i_runtime); + return 0; +} + +void test() { + A a_dependent; + // expected-note@-1+ {{declared here}} + + int Int = 0; + auto l_dependent = [Int](int i) consteval { + // expected-note@-1+ {{declared here}} + return Int + i; + // expected-note@-1 {{is not allowed in a constant expression}} + }; + + int i_l = l_dependent(0); + // expected-error@-1 {{could not be evaluated}} + // expected-note@-2 {{in call}} + + int i_m = a_dependent.f_eval(0); + // expected-error@-1 {{could not be evaluated}} + // expected-note@-2 {{in call}} +} + +} + +namespace taking_address { + +using func_type = int(int); +using mem_ptr_type = int(A::*)(int); + +func_type* p1 = (&f_eval); +// expected-error@-1 {{take address}} +func_type* p2= &(((f_eval))); +// expected-error@-1 {{take address}} +func_type* p3 = (func_type*)f_eval; +// expected-error@-1 {{take address}} +func_type* p4 = static_cast(f_eval); +// expected-error@-1 {{take address}} +func_type* p5 = reinterpret_cast(f_eval); +// expected-error@-1 {{take address}} +func_type* p6 = reinterpret_cast(&reinterpret_cast(f_eval)); +// expected-error@-1 {{take address}} +func_type* p7 = __builtin_addressof(f_eval); +// expected-error@-1 {{take address}} + +auto p = f_eval; +// expected-error@-1 {{take address}} + +mem_ptr_type m1 = &A::f_eval; +// expected-error@-1 {{take address}} +auto* l1 = &decltype(l_eval)::operator(); +// expected-error@-1 {{take address}} + +consteval int f(int i) { +// expected-note@-1+ {{declared here}} + return i; +} + +auto ptr = &f; +// expected-error@-1 {{take address}} + +auto f1() { + return &f; +// expected-error@-1 {{take address}} +} + +constexpr auto f2() { + return &f; +// expected-error@-1 {{take address}} +} + +consteval auto f3() { + return &f; +} + +consteval int Overload() { +//expected-note@-1 {{candidate function made ineligible by consteval specifier}} + return 0; +} + +int Overload(int i) { +//expected-note@-1 {{candidate function}} + return i; +} + +auto Ptr = &Overload; +int(*Ptr1)() = &Overload; +// expected-error@-1 {{does not match}} +int(*Ptr2)(int) = &Overload; + +} + +namespace misc { + +consteval int f(int i) { + return i; +} + +using func_type = int(int); + +consteval func_type* f1() { + return &f; +} + +int run(int i) { + return f1()(i); +// expected-error@-1 {{contain pointer on consteval declaration}} +} + +struct A { + mutable func_type *tmp = nullptr; + consteval int f1() const { +// expected-note@-1 {{declared here}} + tmp = &f; +// expected-note@-1 {{cannot modify}} + return 0; + } +}; + +constexpr A a; + +int v(int i) { + a.f1(); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{in call}} + return a.tmp(i); +} + +struct B { + func_type *tmp = nullptr; + consteval B() { + tmp = &f; + } +}; + +int v2(int i) { + constexpr B b; +// expected-error@-1 {{contain pointer on consteval declaration}} + return b.tmp(i); +} + +} + +namespace invalid_function { +using size_t = unsigned long; +struct A { + consteval void *operator new(size_t count); + // expected-error@-1 {{operator new cannot be consteval}} + consteval void *operator new[](size_t count); + // expected-error@-1 {{operator new[] cannot be consteval}} + consteval void operator delete(void* ptr); + // expected-error@-1 {{operator delete cannot be consteval}} + consteval void operator delete[](void* ptr); + // expected-error@-1 {{operator delete[] cannot be consteval}} + consteval ~A(); + // expected-error@-1 {{destructor cannot be marked consteval}} +}; + +} + +namespace Ctor { + +struct A { + int i; + float f; + consteval A(int i, float f) : i(i), f(f) {} + //expected-note@-1 {{declared here}} + consteval A(const A&) = default; + //expected-note@-1 {{declared here}} + consteval A(A&&) = default; + //expected-note@-1 {{declared here}} +}; + +void test(int i) { + //expected-note@-1 {{declared here}} + A a(i, 0); + //expected-error@-1 {{could not be evaluated}} + //expected-note@-2 {{is not allowed in a constant expression}} + A a1(0, 1); + //expected-note@-1+ {{declared here}} + A a3(a1); + //expected-error@-1 {{could not be evaluated}} + //expected-note@-2 {{is not allowed in a constant expression}} + //expected-note@-3 {{in call}} + A a4(static_cast(a1)); + //expected-error@-1 {{could not be evaluated}} + //expected-note@-2 {{is not allowed in a constant expression}} + //expected-note@-3 {{in call}} + constexpr A a5(0, 4); + A a6(a5); +} + +} \ No newline at end of file