Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -1856,7 +1856,7 @@ FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, bool isInlineSpecified, - bool isConstexprSpecified); + bool isConstexprSpecified, bool isConstevalSpecified); using redeclarable_base = Redeclarable; @@ -1886,29 +1886,23 @@ using redeclarable_base::getMostRecentDecl; using redeclarable_base::isFirstDecl; - static FunctionDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation StartLoc, SourceLocation NLoc, - DeclarationName N, QualType T, - TypeSourceInfo *TInfo, - StorageClass SC, - bool isInlineSpecified = false, - bool hasWrittenPrototype = true, - bool isConstexprSpecified = false) { + static FunctionDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + SourceLocation NLoc, DeclarationName N, QualType T, + TypeSourceInfo *TInfo, StorageClass SC, bool isInlineSpecified = false, + bool hasWrittenPrototype = true, bool isConstexprSpecified = false, + bool isConstevalSpecified = false) { DeclarationNameInfo NameInfo(N, NLoc); - return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, - SC, + return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC, isInlineSpecified, hasWrittenPrototype, - isConstexprSpecified); + isConstexprSpecified, isConstevalSpecified); } - static FunctionDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - StorageClass SC, - bool isInlineSpecified, - bool hasWrittenPrototype, - bool isConstexprSpecified = false); + static FunctionDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, + StorageClass SC, bool isInlineSpecified, bool hasWrittenPrototype, + bool isConstexprSpecified = false, bool isConstevalSpecified = false); static FunctionDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2108,6 +2102,12 @@ bool isConstexpr() const { return FunctionDeclBits.IsConstexpr; } void setConstexpr(bool IC) { FunctionDeclBits.IsConstexpr = IC; } + /// Whether this is a (C++2a) consteval function or consteval constructor. + bool isConsteval() const { return FunctionDeclBits.IsConsteval; } + void setConsteval(bool IC) { FunctionDeclBits.IsConsteval = IC; } + + bool isConstexprOrConsteval() const { return isConstexpr() || isConsteval(); } + /// Whether the instantiation of this function is pending. /// This bit is set when the decision to instantiate this function is made /// and unset if and when the function body is created. That leaves out Index: clang/include/clang/AST/DeclBase.h =================================================================== --- clang/include/clang/AST/DeclBase.h +++ clang/include/clang/AST/DeclBase.h @@ -1492,6 +1492,7 @@ uint64_t HasImplicitReturnZero : 1; uint64_t IsLateTemplateParsed : 1; uint64_t IsConstexpr : 1; + uint64_t IsConsteval : 1; uint64_t InstantiationIsPending : 1; /// Indicates if the function uses __try. @@ -1519,7 +1520,7 @@ }; /// Number of non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = 24 }; + enum { NumFunctionDeclBits = 25 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1536,7 +1537,7 @@ /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 24; + uint64_t NumCtorInitializers : 23; uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -2056,7 +2056,7 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, SourceLocation EndLocation) : FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo, - SC_None, false, false), + SC_None, false, false, false), ExplicitSpec(ES) { if (EndLocation.isValid()) setRangeEnd(EndLocation); @@ -2111,11 +2111,11 @@ protected: CXXMethodDecl(Kind DK, ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - StorageClass SC, bool isInline, - bool isConstexpr, SourceLocation EndLocation) - : FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, - SC, isInline, isConstexpr) { + QualType T, TypeSourceInfo *TInfo, StorageClass SC, + bool isInline, bool isConstexpr, bool isConsteval, + SourceLocation EndLocation) + : FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, SC, isInline, + isConstexpr, isConsteval) { if (EndLocation.isValid()) setRangeEnd(EndLocation); } @@ -2123,12 +2123,10 @@ public: static CXXMethodDecl *Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - StorageClass SC, - bool isInline, - bool isConstexpr, - SourceLocation EndLocation); + const DeclarationNameInfo &NameInfo, QualType T, + TypeSourceInfo *TInfo, StorageClass SC, + bool isInline, bool isConstexpr, + bool isConsteval, SourceLocation EndLocation); static CXXMethodDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2564,7 +2562,7 @@ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, bool isConstexpr, - InheritedConstructor Inherited); + bool isConsteval, InheritedConstructor Inherited); void anchor() override; @@ -2616,7 +2614,7 @@ Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, - bool isConstexpr, + bool isConstexpr, bool isConsteval, InheritedConstructor Inherited = InheritedConstructor()); ExplicitSpecifier getExplicitSpecifier() { @@ -2822,12 +2820,12 @@ Expr *OperatorDeleteThisArg = nullptr; CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared) - : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, /*isConstexpr=*/false, SourceLocation()) - { + const DeclarationNameInfo &NameInfo, QualType T, + TypeSourceInfo *TInfo, bool isInline, + bool isImplicitlyDeclared) + : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, + SC_None, isInline, /*isConstexpr=*/false, + /*isConsteval=*/false, SourceLocation()) { setImplicit(isImplicitlyDeclared); } @@ -2878,9 +2876,10 @@ CXXConversionDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES, - bool isConstexpr, SourceLocation EndLocation) + bool isConstexpr, bool isConsteval, + SourceLocation EndLocation) : CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, isConstexpr, EndLocation), + SC_None, isInline, isConstexpr, isConsteval, EndLocation), ExplicitSpec(ES) {} void anchor() override; @@ -2896,7 +2895,7 @@ Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES, bool isConstexpr, - SourceLocation EndLocation); + bool isConsteval, SourceLocation EndLocation); static CXXConversionDecl *CreateDeserialized(ASTContext &C, unsigned ID); ExplicitSpecifier getExplicitSpecifier() { Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -355,10 +355,8 @@ "type name requires a specifier or qualifier">; def err_typename_invalid_storageclass : Error< "type name does not allow storage class to be specified">; -def err_typename_invalid_functionspec : Error< - "type name does not allow function specifier to be specified">; -def err_typename_invalid_constexpr : Error< - "type name does not allow constexpr specifier to be specified">; +def err_typename_invalid_specifier : Error< + "type name does not allow %0 specifier to be specified">; def err_typename_identifiers_only : Error< "typename is allowed for identifiers only">; @@ -873,9 +871,9 @@ InGroup, DefaultIgnore; def err_lambda_missing_parens : Error< "lambda requires '()' before %select{'mutable'|return type|" - "attribute specifier|'constexpr'}0">; + "attribute specifier|'constexpr'|'consteval'}0">; def err_lambda_decl_specifier_repeated : Error< - "%select{'mutable'|'constexpr'}0 cannot appear multiple times in a lambda declarator">; + "%select{'mutable'|'constexpr'|'consteval'}0 cannot appear multiple times in a lambda declarator">; // C++17 lambda expressions def err_expected_star_this_capture : Error< "expected 'this' following '*' in lambda capture list">; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -695,7 +695,7 @@ "'main' is not allowed to be declared _Noreturn">, InGroup
; def note_main_remove_noreturn : Note<"remove '_Noreturn'">; def err_constexpr_main : Error< - "'main' is not allowed to be declared constexpr">; + "'main' is not allowed to be declared %select{constexpr|consteval}0">; def err_deleted_main : Error<"'main' is not allowed to be deleted">; def err_mainlike_template_decl : Error<"%0 cannot be a template">; def err_main_returns_nonint : Error<"'main' must return 'int'">; @@ -2283,7 +2283,7 @@ "initialization statement is not supported when iterating over Objective-C " "collection">; -// C++11 constexpr +// C++11 constexpr and C++2a consteval def warn_cxx98_compat_constexpr : Warning< "'constexpr' specifier is incompatible with C++98">, InGroup, DefaultIgnore; @@ -2292,6 +2292,14 @@ "'constexpr' non-static member function will not be implicitly 'const' " "in C++14; add 'const' to avoid a change in behavior">, InGroup>; +def err_incompatible_constexpr_consteval : Error< + "constexpr specifier is incompatible with consteval specifier">; +def err_consteval_cannot_be_constant_eval : Error< + "call to %0 cannot be constant evaluated">; +def note_argument_n_cannot_be_constant_eval : Note< + "argument %0 cannot be constant evaluated">; +def note_not_constexpr_this : Note< + "call to member of non-constexpr value">; def err_invalid_constexpr : Error< "%select{function parameter|typedef|non-static data member}0 " "cannot be constexpr">; @@ -2299,7 +2307,11 @@ "constexpr%select{; did you intend to make it %select{const|static}0?|}1">; def err_constexpr_tag : Error< "%select{class|struct|interface|union|enum}0 cannot be marked constexpr">; -def err_constexpr_dtor : Error<"destructor cannot be marked constexpr">; +def err_constexpr_dtor : Error<"destructor cannot be marked %0">; +def err_invalid_consteval_decl_kind : Error< + "operator %select{new|delete|new[]|delete[]}0 cannot be marked consteval">; +def err_take_adress_of_consteval_decl : Error< + "taking address of a %0">; def err_constexpr_no_declarators : Error< "constexpr can only be used in variable and function declarations">; def err_invalid_constexpr_var_decl : Error< @@ -2311,8 +2323,8 @@ def err_constexpr_var_requires_const_init : Error< "constexpr variable %0 must be initialized by a constant expression">; def err_constexpr_redecl_mismatch : Error< - "%select{non-constexpr declaration of %0 follows constexpr declaration" - "|constexpr declaration of %0 follows non-constexpr declaration}1">; + "%select{non-%2 declaration of %0 follows %2 declaration" + "|%2 declaration of %0 follows non-%2 declaration}1">; def err_constexpr_virtual : Error<"virtual function cannot be constexpr">; def err_constexpr_virtual_base : Error< "constexpr %select{member function|constructor}0 not allowed in " @@ -9380,7 +9392,7 @@ "'%1' cannot be used in %select{a constructor|a destructor" "|a copy assignment operator|a move assignment operator|the 'main' function" "|a constexpr function|a function with a deduced return type" - "|a varargs function}0">; + "|a varargs function|a consteval function}0">; def err_implied_coroutine_type_not_found : Error< "%0 type was not found; include before defining " "a coroutine">; @@ -9618,7 +9630,7 @@ "attribute '%select{target|cpu_specific|cpu_dispatch}0' multiversioned functions do not " "yet support %select{function templates|virtual functions|" "deduced return types|constructors|destructors|deleted functions|" - "defaulted functions|constexpr functions}1">; + "defaulted functions|constexpr functions|consteval functions}1">; def err_multiversion_not_allowed_on_main : Error< "'main' cannot be a multiversioned function">; def err_multiversion_not_supported : Error< Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -387,6 +387,9 @@ // C++ char8_t proposal CXX2A_KEYWORD(char8_t , CHAR8SUPPORT) +// C++ consteval proposal +CXX2A_KEYWORD(consteval , 0) + // C11 Extension KEYWORD(_Float16 , KEYALL) Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -365,6 +365,9 @@ // constexpr-specifier unsigned Constexpr_specified : 1; + // consteval-specifier + unsigned Consteval_specified : 1; + union { UnionParsedType TypeRep; Decl *DeclRep; @@ -398,7 +401,7 @@ SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc, FS_noreturnLoc; SourceLocation FS_explicitCloseParenLoc; SourceLocation FS_forceinlineLoc; - SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; + SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc, ConstevalLoc; SourceLocation TQ_pipeLoc; WrittenBuiltinSpecs writtenBS; @@ -434,8 +437,8 @@ FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), FS_noreturn_specified(false), Friend_specified(false), Constexpr_specified(false), - FS_explicit_specifier(), Attrs(attrFactory), writtenBS(), - ObjCQualifiers(nullptr) {} + Consteval_specified(false), FS_explicit_specifier(), Attrs(attrFactory), + writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; } @@ -721,6 +724,9 @@ bool SetConstexprSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); + bool SetConstevalSpec(SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID); + bool isFriendSpecified() const { return Friend_specified; } SourceLocation getFriendSpecLoc() const { return FriendLoc; } @@ -735,6 +741,14 @@ ConstexprLoc = SourceLocation(); } + bool isConstevalSpecified() const { return Consteval_specified; } + SourceLocation getConstevalSpecLoc() const { return ConstevalLoc; } + + void ClearConstevalSpec() { + Consteval_specified = false; + ConstevalLoc = SourceLocation(); + } + AttributePool &getAttributePool() const { return Attrs.getPool(); } Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -978,9 +978,12 @@ /// fields, such as a SIZE operator in MS-style inline assembly. UnevaluatedAbstract, - /// The current context is "potentially evaluated" in C++11 terms, - /// but the expression is evaluated at compile-time (like the values of - /// cases in a switch statement). + /// The current context is either of: + /// - "potentially evaluated" in C++11 terms, + /// but the expression is evaluated at compile-time (like the values of + /// cases in a switch statement). + /// - "immediate function context" in C++2a terms, a call to a function + /// marked as consteval ConstantEvaluated, /// The current expression is potentially evaluated at run time, @@ -4046,6 +4049,16 @@ void DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock); + /// CheckInvalidConstevalCall - detect and diagnose invalid calls to + /// consteval function. + /// return true if the call is invalid. + bool CheckInvalidConstevalCall(FunctionDecl *FDecl, SourceLocation Loc, + ArrayRef Args, Expr *This = nullptr); + + /// CheckInvalidConstevalTakeAddress - detect and diagnose if Input is a + /// DeclRef to a consteval function. return true if the operation is forbiden. + bool CheckInvalidConstevalTakeAddress(Expr *Input); + bool ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const; /// If it's a file scoped decl that must warn if not used, keep track @@ -5678,12 +5691,11 @@ LambdaCaptureDefault CaptureDefault); /// Start the definition of a lambda expression. - CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodType, - SourceLocation EndLoc, - ArrayRef Params, - bool IsConstexprSpecified); + CXXMethodDecl * + startLambdaDefinition(CXXRecordDecl *Class, SourceRange IntroducerRange, + TypeSourceInfo *MethodType, SourceLocation EndLoc, + ArrayRef Params, + bool IsConstexprSpecified, bool IsConstevalSpecified); /// Endow the lambda scope info with the relevant properties. void buildLambdaScope(sema::LambdaScopeInfo *LSI, @@ -9610,11 +9622,12 @@ /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit /// cast. If there is already an implicit cast, merge into the existing one. /// If isLvalue, the result of the cast is an lvalue. - ExprResult ImpCastExprToType(Expr *E, QualType Type, CastKind CK, - ExprValueKind VK = VK_RValue, - const CXXCastPath *BasePath = nullptr, - CheckedConversionKind CCK - = CCK_ImplicitConversion); + ExprResult + ImpCastExprToType(Expr *E, QualType Type, CastKind CK, + ExprValueKind VK = VK_RValue, + const CXXCastPath *BasePath = nullptr, + CheckedConversionKind CCK = CCK_ImplicitConversion, + bool isCastForCall = false); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3089,7 +3089,8 @@ ExplicitSpecifier( ExplicitExpr, FromConstructor->getExplicitSpecifier().getKind()), - D->isInlineSpecified(), D->isImplicit(), D->isConstexpr())) + D->isInlineSpecified(), D->isImplicit(), D->isConstexpr(), + D->isConsteval())) return ToFunction; } else if (CXXDestructorDecl *FromDtor = dyn_cast(D)) { @@ -3127,19 +3128,20 @@ ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), ExplicitSpecifier(ExplicitExpr, FromConversion->getExplicitSpecifier().getKind()), - D->isConstexpr(), SourceLocation())) + D->isConstexpr(), D->isConsteval(), SourceLocation())) return ToFunction; } else if (auto *Method = dyn_cast(D)) { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, Method->getStorageClass(), - Method->isInlineSpecified(), D->isConstexpr(), SourceLocation())) + Method->isInlineSpecified(), D->isConstexpr(), D->isConsteval(), + SourceLocation())) return ToFunction; } else { - if (GetImportedOrCreateDecl(ToFunction, D, Importer.getToContext(), DC, - ToInnerLocStart, NameInfo, T, TInfo, - D->getStorageClass(), D->isInlineSpecified(), - D->hasWrittenPrototype(), D->isConstexpr())) + if (GetImportedOrCreateDecl( + ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, + NameInfo, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), + D->hasWrittenPrototype(), D->isConstexpr(), D->isConsteval())) return ToFunction; } Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -2704,7 +2704,8 @@ SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, - bool isInlineSpecified, bool isConstexprSpecified) + bool isInlineSpecified, bool isConstexprSpecified, + bool isConstevalSpecified) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), redeclarable_base(C), ODRHash(0), @@ -2725,6 +2726,7 @@ FunctionDeclBits.HasImplicitReturnZero = false; FunctionDeclBits.IsLateTemplateParsed = false; FunctionDeclBits.IsConstexpr = isConstexprSpecified; + FunctionDeclBits.IsConsteval = isConstevalSpecified; FunctionDeclBits.InstantiationIsPending = false; FunctionDeclBits.UsesSEHTry = false; FunctionDeclBits.HasSkippedBody = false; @@ -4517,17 +4519,15 @@ return new (C, ID) ImplicitParamDecl(C, QualType(), ImplicitParamKind::Other); } -FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - StorageClass SC, - bool isInlineSpecified, - bool hasWrittenPrototype, - bool isConstexprSpecified) { - FunctionDecl *New = - new (C, DC) FunctionDecl(Function, C, DC, StartLoc, NameInfo, T, TInfo, - SC, isInlineSpecified, isConstexprSpecified); +FunctionDecl * +FunctionDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, QualType T, + TypeSourceInfo *TInfo, StorageClass SC, + bool isInlineSpecified, bool hasWrittenPrototype, + bool isConstexprSpecified, bool isConstevalSpecified) { + FunctionDecl *New = new (C, DC) FunctionDecl( + Function, C, DC, StartLoc, NameInfo, T, TInfo, SC, isInlineSpecified, + isConstexprSpecified, isConstevalSpecified); New->setHasWrittenPrototype(hasWrittenPrototype); return New; } @@ -4535,7 +4535,7 @@ FunctionDecl *FunctionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) FunctionDecl(Function, C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - SC_None, false, false); + SC_None, false, false, false); } BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) { Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -730,7 +730,7 @@ if (Constructor->isUserProvided()) data().UserProvidedDefaultConstructor = true; - if (Constructor->isConstexpr()) + if (Constructor->isConstexprOrConsteval()) data().HasConstexprDefaultConstructor = true; if (Constructor->isDefaulted()) data().HasDefaultedDefaultConstructor = true; @@ -767,7 +767,8 @@ // [...] has at least one constexpr constructor or constructor template // (possibly inherited from a base class) that is not a copy or move // constructor [...] - if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) + if ((Constructor->isConstexprOrConsteval()) && + !Constructor->isCopyOrMoveConstructor()) data().HasConstexprNonCopyMoveConstructor = true; } @@ -1301,7 +1302,7 @@ if (const auto *Constructor = dyn_cast(D)) { if (Constructor->isDefaultConstructor()) { SMKind |= SMF_DefaultConstructor; - if (Constructor->isConstexpr()) + if (Constructor->isConstexprOrConsteval()) data().HasConstexprDefaultConstructor = true; } if (Constructor->isCopyConstructor()) @@ -1986,22 +1987,22 @@ return nullptr; } -CXXMethodDecl * -CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, - SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - StorageClass SC, bool isInline, - bool isConstexpr, SourceLocation EndLocation) { - return new (C, RD) CXXMethodDecl(CXXMethod, C, RD, StartLoc, NameInfo, - T, TInfo, SC, isInline, isConstexpr, - EndLocation); +CXXMethodDecl *CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, + QualType T, TypeSourceInfo *TInfo, + StorageClass SC, bool isInline, + bool isConstexpr, bool isConsteval, + SourceLocation EndLocation) { + return new (C, RD) + CXXMethodDecl(CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, + isInline, isConstexpr, isConsteval, EndLocation); } CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) CXXMethodDecl(CXXMethod, C, nullptr, SourceLocation(), - DeclarationNameInfo(), QualType(), nullptr, - SC_None, false, false, SourceLocation()); + return new (C, ID) CXXMethodDecl( + CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(), + QualType(), nullptr, SC_None, false, false, false, SourceLocation()); } CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, @@ -2358,9 +2359,10 @@ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, - bool isConstexpr, InheritedConstructor Inherited) + bool isConstexpr, bool isConsteval, InheritedConstructor Inherited) : CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, isConstexpr, SourceLocation()) { + SC_None, isInline, isConstexpr, isConsteval, + SourceLocation()) { setNumCtorInitializers(0); setInheritingConstructor(static_cast(Inherited)); setImplicit(isImplicitlyDeclared); @@ -2383,7 +2385,7 @@ isInheritingConstructor, hasTraillingExplicit); auto *Result = new (C, ID, Extra) CXXConstructorDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - ExplicitSpecifier(), false, false, false, InheritedConstructor()); + ExplicitSpecifier(), false, false, false, false, InheritedConstructor()); Result->setInheritingConstructor(isInheritingConstructor); Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier = hasTraillingExplicit; @@ -2395,16 +2397,16 @@ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared, - bool isConstexpr, InheritedConstructor Inherited) { + bool isConstexpr, bool isConsteval, InheritedConstructor Inherited) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName && "Name must refer to a constructor"); unsigned Extra = additionalSizeToAlloc( Inherited ? 1 : 0, ES.getExpr() ? 1 : 0); - return new (C, RD, Extra) - CXXConstructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, ES, isInline, - isImplicitlyDeclared, isConstexpr, Inherited); + return new (C, RD, Extra) CXXConstructorDecl( + C, RD, StartLoc, NameInfo, T, TInfo, ES, isInline, isImplicitlyDeclared, + isConstexpr, isConsteval, Inherited); } CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const { @@ -2557,19 +2559,20 @@ CXXConversionDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXConversionDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - false, ExplicitSpecifier(), false, SourceLocation()); + false, ExplicitSpecifier(), false, false, SourceLocation()); } CXXConversionDecl *CXXConversionDecl::Create( ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, - bool isInline, ExplicitSpecifier ES, bool isConstexpr, + bool isInline, ExplicitSpecifier ES, bool isConstexpr, bool isConsteval, SourceLocation EndLocation) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConversionFunctionName && "Name must refer to a conversion function"); - return new (C, RD) CXXConversionDecl(C, RD, StartLoc, NameInfo, T, TInfo, - isInline, ES, isConstexpr, EndLocation); + return new (C, RD) + CXXConversionDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline, ES, + isConstexpr, isConsteval, EndLocation); } bool CXXConversionDecl::isLambdaToBlockPointerConversion() const { Index: clang/lib/AST/DeclPrinter.cpp =================================================================== --- clang/lib/AST/DeclPrinter.cpp +++ clang/lib/AST/DeclPrinter.cpp @@ -610,7 +610,10 @@ if (D->isInlineSpecified()) Out << "inline "; if (D->isVirtualAsWritten()) Out << "virtual "; if (D->isModulePrivate()) Out << "__module_private__ "; - if (D->isConstexpr() && !D->isExplicitlyDefaulted()) Out << "constexpr "; + if (D->isConstexpr() && !D->isExplicitlyDefaulted()) + Out << "constexpr "; + if (D->isConsteval() && !D->isExplicitlyDefaulted()) + Out << "consteval "; ExplicitSpecifier ExplicitSpec = ExplicitSpecifier::getFromDecl(D); if (ExplicitSpec.isSpecified()) printExplicitSpecifier(ExplicitSpec, Out, Policy, Indentation); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -777,6 +777,20 @@ EM_PotentialConstantExpressionUnevaluated, } EvalMode; + /// EnterConstantContextRAII - RAII type to change + /// InConstantContext and restore it. for consteval function calls. + struct EnterConstantContextRAII { + EvalInfo &Info; + bool IsPreviousContextConstant; + EnterConstantContextRAII(EvalInfo &EI, bool IsConstantContext) + : Info(EI), IsPreviousContextConstant(EI.InConstantContext) { + Info.InConstantContext = IsConstantContext; + } + ~EnterConstantContextRAII() { + Info.InConstantContext = IsPreviousContextConstant; + } + }; + /// Are we checking whether the expression is a potential constant /// expression? bool checkingPotentialConstantExpression() const { @@ -4347,7 +4361,7 @@ // Value-initialization does not call a trivial default constructor, so such a // call is a core constant expression whether or not the constructor is // constexpr. - if (!CD->isConstexpr() && !IsValueInitialization) { + if (!CD->isConstexprOrConsteval() && !IsValueInitialization) { if (Info.getLangOpts().CPlusPlus11) { // FIXME: If DiagDecl is an implicitly-declared special member function, // we should be much more explicit about why it's not constexpr. @@ -4370,7 +4384,7 @@ // Potential constant expressions can contain calls to declared, but not yet // defined, constexpr functions. if (Info.checkingPotentialConstantExpression() && !Definition && - Declaration->isConstexpr()) + Declaration->isConstexprOrConsteval()) return false; // Bail out if the function declaration itself is invalid. We will @@ -4382,7 +4396,7 @@ } // Can we evaluate this function call? - if (Definition && Definition->isConstexpr() && + if (Definition && Definition->isConstexprOrConsteval() && !Definition->isInvalidDecl() && Body) return true; @@ -4394,7 +4408,7 @@ auto *CD = dyn_cast(DiagDecl); if (CD && CD->isInheritingConstructor()) { auto *Inherited = CD->getInheritedConstructor().getConstructor(); - if (!Inherited->isConstexpr()) + if (!Inherited->isConstexprOrConsteval()) DiagDecl = CD = Inherited; } @@ -4406,7 +4420,7 @@ << CD->getInheritedConstructor().getConstructor()->getParent(); else Info.FFDiag(CallLoc, diag::note_constexpr_invalid_function, 1) - << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + << DiagDecl->isConstexprOrConsteval() << (bool)CD << DiagDecl; Info.Note(DiagDecl->getLocation(), diag::note_declared_at); } else { Info.FFDiag(CallLoc, diag::note_invalid_subexpr_in_const_expr); @@ -4457,6 +4471,8 @@ ArrayRef Args, const Stmt *Body, EvalInfo &Info, APValue &Result, const LValue *ResultSlot) { + EvalInfo::EnterConstantContextRAII ContextRAII( + Info, Callee->isConsteval() || Info.InConstantContext); ArgVector ArgValues(Args.size()); if (!EvaluateArgs(Args, ArgValues, Info)) return false; @@ -4672,6 +4688,8 @@ ArrayRef Args, const CXXConstructorDecl *Definition, EvalInfo &Info, APValue &Result) { + EvalInfo::EnterConstantContextRAII ContextRAII( + Info, Definition->isConsteval() || Info.InConstantContext); ArgVector ArgValues(Args.size()); if (!EvaluateArgs(Args, ArgValues, Info)) return false; Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -255,9 +255,12 @@ if (D->isInvalidDecl()) OS << " invalid"; - if (const FunctionDecl *FD = dyn_cast(D)) + if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->isConstexpr()) OS << " constexpr"; + if (FD->isConsteval()) + OS << " consteval"; + } if (!isa(*D)) { const auto *MD = dyn_cast(D); Index: clang/lib/Analysis/ReachableCode.cpp =================================================================== --- clang/lib/Analysis/ReachableCode.cpp +++ clang/lib/Analysis/ReachableCode.cpp @@ -213,7 +213,7 @@ case Stmt::CallExprClass: { const FunctionDecl *Callee = dyn_cast_or_null(cast(S)->getCalleeDecl()); - return Callee ? Callee->isConstexpr() : false; + return Callee ? Callee->isConstexprOrConsteval() : false; } case Stmt::DeclRefExprClass: return isConfigurationValue(cast(S)->getDecl(), PP); Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -18,6 +18,7 @@ #include "CGCleanup.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "ConstantEmitter.h" #include "TargetInfo.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -26,6 +27,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/SwiftCallingConv.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Transforms/Utils/Local.h" @@ -3773,6 +3775,44 @@ DeferredReplacements.push_back(std::make_pair(Old, New)); } +RValue CodeGenFunction::EmitConstevalExpr(const Expr *E, Address DestPtr, + QualType RetType) { + Expr::EvalResult Result; + bool EvalReturn; + if (RetType->isReferenceType()) + EvalReturn = E->EvaluateAsLValue(Result, CGM.getContext()); + else + EvalReturn = E->EvaluateAsRValue(Result, CGM.getContext()); + + llvm::Constant *Constant = nullptr; + if (EvalReturn) + Constant = ConstantEmitter(*this).emitAbstract(E->getBeginLoc(), Result.Val, + RetType); + // Pointer and References to local variable cannot be evaluated but are valid. + // so they are set to null. + else if (RetType->isReferenceType() || RetType->isPointerType()) + Constant = llvm::Constant::getNullValue( + CodeGen::convertTypeForMemory(CGM, RetType)); + else + llvm_unreachable("call to consteval function cannot be evaluated. should " + "have been removed by Sema"); + + switch (getEvaluationKind(RetType)) { + case TEK_Scalar: + return RValue::get(Constant); + case TEK_Complex: + return RValue::getComplex(Constant->getAggregateElement(0u), + Constant->getAggregateElement(1u)); + case TEK_Aggregate: + if (!DestPtr.isValid()) + DestPtr = CreateMemTemp(RetType.getNonReferenceType(), "ret.tmp"); + BuildAggStore(*this, Constant, DestPtr, false); + if (RetType->isReferenceType()) + return RValue::get(RValue::getAggregate(DestPtr).getAggregatePointer()); + return RValue::getAggregate(DestPtr); + } +} + RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, const CGCallee &Callee, ReturnValueSlot ReturnValue, Index: clang/lib/CodeGen/CGClass.cpp =================================================================== --- clang/lib/CodeGen/CGClass.cpp +++ clang/lib/CodeGen/CGClass.cpp @@ -2029,6 +2029,11 @@ LangAS ThisAS = ThisType.getTypePtr()->getPointeeType().getAddressSpace(); llvm::Value *ThisPtr = This.getPointer(); + if (D->isConsteval()) { + EmitConstevalExpr(E, This, ThisType->getPointeeType()); + return; + } + if (SlotAS != ThisAS) { unsigned TargetThisAS = getContext().getTargetAddressSpace(ThisAS); llvm::Type *NewType = Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4351,6 +4351,11 @@ if (const auto *CE = dyn_cast(E)) return EmitCUDAKernelCallExpr(CE, ReturnValue); + if (const auto *FDecl = dyn_cast_or_null(E->getCalleeDecl())) + if (FDecl->isConsteval()) + return EmitConstevalExpr(E, ReturnValue.getValue(), + E->getCallReturnType(CGM.getContext())); + if (const auto *CE = dyn_cast(E)) if (const CXXMethodDecl *MD = dyn_cast_or_null(CE->getCalleeDecl())) Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -3590,6 +3590,7 @@ ReturnValueSlot ReturnValue, llvm::Value *Chain = nullptr); RValue EmitCallExpr(const CallExpr *E, ReturnValueSlot ReturnValue = ReturnValueSlot()); + RValue EmitConstevalExpr(const Expr *E, Address DestPtr, QualType RetType); RValue EmitSimpleCallExpr(const CallExpr *E, ReturnValueSlot ReturnValue); CGCallee EmitCallee(const Expr *E); Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2477,18 +2477,29 @@ // Issue diagnostic and remove function specifier if present. if (Specs & DeclSpec::PQ_FunctionSpecifier) { if (DS.isInlineSpecified()) - Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec); + Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_specifier) + << "function"; if (DS.isVirtualSpecified()) - Diag(DS.getVirtualSpecLoc(), diag::err_typename_invalid_functionspec); + Diag(DS.getVirtualSpecLoc(), diag::err_typename_invalid_specifier) + << "function"; if (DS.hasExplicitSpecifier()) - Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_functionspec); + Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_specifier) + << "function"; DS.ClearFunctionSpecs(); } - // Issue diagnostic and remove constexpr specifier if present. - if (DS.isConstexprSpecified() && DSC != DeclSpecContext::DSC_condition) { - Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_constexpr); - DS.ClearConstexprSpec(); + // Issue diagnostic and remove constexpr or consteval specifier if present. + if (DSC != DeclSpecContext::DSC_condition) { + if (DS.isConstexprSpecified()) { + Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_specifier) + << "constexpr"; + DS.ClearConstexprSpec(); + } + if (DS.isConstevalSpecified()) { + Diag(DS.getConstevalSpecLoc(), diag::err_typename_invalid_specifier) + << "consteval"; + DS.ClearConstevalSpec(); + } } } @@ -2982,6 +2993,7 @@ /// [OpenCL] '__kernel' /// 'friend': [C++ dcl.friend] /// 'constexpr': [C++0x dcl.constexpr] +/// 'consteval': [C++2a dcl.constexpr] void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS, @@ -3624,6 +3636,10 @@ isInvalid = DS.SetConstexprSpec(Loc, PrevSpec, DiagID); break; + case tok::kw_consteval: + isInvalid = DS.SetConstevalSpec(Loc, PrevSpec, DiagID); + break; + // type-specifier case tok::kw_short: isInvalid = DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec, @@ -5027,6 +5043,9 @@ case tok::annot_decltype: case tok::kw_constexpr: + // C++20 consteval. + case tok::kw_consteval: + // C11 _Atomic case tok::kw__Atomic: return true; Index: clang/lib/Parse/ParseExprCXX.cpp =================================================================== --- clang/lib/Parse/ParseExprCXX.cpp +++ clang/lib/Parse/ParseExprCXX.cpp @@ -1038,10 +1038,11 @@ return true; } -static void -tryConsumeMutableOrConstexprToken(Parser &P, SourceLocation &MutableLoc, - SourceLocation &ConstexprLoc, - SourceLocation &DeclEndLoc) { +static void tryConsumeLambdaSpecifierToken(Parser &P, + SourceLocation &MutableLoc, + SourceLocation &ConstexprLoc, + SourceLocation &ConstevalLoc, + SourceLocation &DeclEndLoc) { assert(MutableLoc.isInvalid()); assert(ConstexprLoc.isInvalid()); // Consume constexpr-opt mutable-opt in any sequence, and set the DeclEndLoc @@ -1069,6 +1070,20 @@ ConstexprLoc = P.ConsumeToken(); DeclEndLoc = ConstexprLoc; break /*switch*/; + case tok::identifier: + if (P.getCurToken().getIdentifierInfo()->getName() != "consteval") + return; + // Diagnostic has already been emitted by the Lexer. + LLVM_FALLTHROUGH; + case tok::kw_consteval: + if (ConstevalLoc.isValid()) { + P.Diag(P.getCurToken().getLocation(), + diag::err_lambda_decl_specifier_repeated) + << 2 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); + } + ConstevalLoc = P.ConsumeToken(); + DeclEndLoc = ConstevalLoc; + break /*switch*/; default: return; } @@ -1090,6 +1105,18 @@ } } +static void addConstevalToLambdaDeclSpecifier(Parser &P, + SourceLocation ConstevalLoc, + DeclSpec &DS) { + if (ConstevalLoc.isValid()) { + const char *PrevSpec = nullptr; + unsigned DiagID = 0; + DS.SetConstevalSpec(ConstevalLoc, PrevSpec, DiagID); + assert(PrevSpec == nullptr && DiagID == 0 && + "Consteval cannot have been set previously!"); + } +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1200,14 +1227,17 @@ // compatible with MSVC. MaybeParseMicrosoftDeclSpecs(Attr, &DeclEndLoc); - // Parse mutable-opt and/or constexpr-opt, and update the DeclEndLoc. + // Parse mutable-opt and/or constexpr-opt or consteval-opt, and update the + // DeclEndLoc. SourceLocation MutableLoc; SourceLocation ConstexprLoc; - tryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc, - DeclEndLoc); + SourceLocation ConstevalLoc; + tryConsumeLambdaSpecifierToken(*this, MutableLoc, ConstexprLoc, + ConstevalLoc, DeclEndLoc); addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS); - + addConstevalToLambdaDeclSpecifier(*this, ConstevalLoc, DS); + DS.Finish(Actions, Actions.getPrintingPolicy()); // Parse exception-specification[opt]. ExceptionSpecificationType ESpecType = EST_None; SourceRange ESpecRange; @@ -1259,7 +1289,7 @@ TrailingReturnType), std::move(Attr), DeclEndLoc); } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute, - tok::kw_constexpr) || + tok::kw_constexpr, tok::kw_consteval) || (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) { // It's common to forget that one needs '()' before 'mutable', an attribute // specifier, or the result type. Deal with this. @@ -1270,6 +1300,9 @@ case tok::kw___attribute: case tok::l_square: TokKind = 2; break; case tok::kw_constexpr: TokKind = 3; break; + case tok::kw_consteval: + TokKind = 4; + break; default: llvm_unreachable("Unknown token kind"); } Index: clang/lib/Parse/ParseTentative.cpp =================================================================== --- clang/lib/Parse/ParseTentative.cpp +++ clang/lib/Parse/ParseTentative.cpp @@ -1197,6 +1197,7 @@ /// 'friend' /// 'typedef' /// [C++11] 'constexpr' +/// [C++20] 'consteval' /// [GNU] attributes declaration-specifiers[opt] /// /// storage-class-specifier: @@ -1379,9 +1380,11 @@ // 'friend' // 'typedef' // 'constexpr' + // 'consteval' case tok::kw_friend: case tok::kw_typedef: case tok::kw_constexpr: + case tok::kw_consteval: // storage-class-specifier case tok::kw_register: case tok::kw_static: Index: clang/lib/Sema/DeclSpec.cpp =================================================================== --- clang/lib/Sema/DeclSpec.cpp +++ clang/lib/Sema/DeclSpec.cpp @@ -1039,6 +1039,18 @@ return false; } +bool DeclSpec::SetConstevalSpec(SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID) { + if (Consteval_specified) { + DiagID = diag::ext_warn_duplicate_declspec; + PrevSpec = "consteval"; + return true; + } + Consteval_specified = true; + ConstevalLoc = Loc; + return false; +} + void DeclSpec::SaveWrittenBuiltinSpecs() { writtenBS.Sign = getTypeSpecSign(); writtenBS.Width = getTypeSpecWidth(); @@ -1311,6 +1323,14 @@ ClearStorageClassSpecs(); } + // C++2a [dcl.spec]p2: The constexpr and consteval decl-specifiers shall not + // both appear in a decl-specifier-seq. + if (isConstevalSpecified() && isConstexprSpecified()) { + S.Diag(ConstexprLoc, diag::err_incompatible_constexpr_consteval) + << FixItHint::CreateRemoval(ConstexprLoc); + Constexpr_specified = false; + } + // C++11 [dcl.fct.spec]p5: // The virtual specifier shall be used only in the initial // declaration of a non-static class member function; Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -483,10 +483,11 @@ /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. /// The result is of the given category. -ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, - CastKind Kind, ExprValueKind VK, +ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, CastKind Kind, + ExprValueKind VK, const CXXCastPath *BasePath, - CheckedConversionKind CCK) { + CheckedConversionKind CCK, + bool isCastForCall) { #ifndef NDEBUG if (VK == VK_RValue && !E->isRValue()) { switch (Kind) { @@ -533,6 +534,10 @@ } } + if (Kind == CK_FunctionToPointerDecay && !isCastForCall && + CheckInvalidConstevalTakeAddress(E)) + return ExprError(); + return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK); } Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -9906,12 +9906,13 @@ /// range of values it might take. /// /// \param MaxWidth - the width to which the value will be truncated -static IntRange GetExprRange(ASTContext &C, const Expr *E, unsigned MaxWidth) { +static IntRange GetExprRange(ASTContext &C, const Expr *E, unsigned MaxWidth, + bool IsConstantContext) { E = E->IgnoreParens(); // Try a full evaluation first. Expr::EvalResult result; - if (E->EvaluateAsRValue(result, C)) + if (E->EvaluateAsRValue(result, C, IsConstantContext)) return GetValueRange(C, result.Val, GetExprType(E), MaxWidth); // I think we only want to look through implicit casts here; if the @@ -9919,7 +9920,7 @@ // being of the new, wider type. if (const auto *CE = dyn_cast(E)) { if (CE->getCastKind() == CK_NoOp || CE->getCastKind() == CK_LValueToRValue) - return GetExprRange(C, CE->getSubExpr(), MaxWidth); + return GetExprRange(C, CE->getSubExpr(), MaxWidth, IsConstantContext); IntRange OutputTypeRange = IntRange::forValueOfType(C, GetExprType(CE)); @@ -9930,9 +9931,9 @@ if (!isIntegerCast) return OutputTypeRange; - IntRange SubRange - = GetExprRange(C, CE->getSubExpr(), - std::min(MaxWidth, OutputTypeRange.Width)); + IntRange SubRange = GetExprRange(C, CE->getSubExpr(), + std::min(MaxWidth, OutputTypeRange.Width), + IsConstantContext); // Bail out if the subexpr's range is as wide as the cast type. if (SubRange.Width >= OutputTypeRange.Width) @@ -9948,13 +9949,15 @@ // If we can fold the condition, just take that operand. bool CondResult; if (CO->getCond()->EvaluateAsBooleanCondition(CondResult, C)) - return GetExprRange(C, CondResult ? CO->getTrueExpr() - : CO->getFalseExpr(), - MaxWidth); + return GetExprRange(C, + CondResult ? CO->getTrueExpr() : CO->getFalseExpr(), + MaxWidth, IsConstantContext); // Otherwise, conservatively merge. - IntRange L = GetExprRange(C, CO->getTrueExpr(), MaxWidth); - IntRange R = GetExprRange(C, CO->getFalseExpr(), MaxWidth); + IntRange L = + GetExprRange(C, CO->getTrueExpr(), MaxWidth, IsConstantContext); + IntRange R = + GetExprRange(C, CO->getFalseExpr(), MaxWidth, IsConstantContext); return IntRange::join(L, R); } @@ -9990,7 +9993,7 @@ // been coerced to the LHS type. case BO_Assign: // TODO: bitfields? - return GetExprRange(C, BO->getRHS(), MaxWidth); + return GetExprRange(C, BO->getRHS(), MaxWidth, IsConstantContext); // Operations with opaque sources are black-listed. case BO_PtrMemD: @@ -10000,8 +10003,9 @@ // Bitwise-and uses the *infinum* of the two source ranges. case BO_And: case BO_AndAssign: - return IntRange::meet(GetExprRange(C, BO->getLHS(), MaxWidth), - GetExprRange(C, BO->getRHS(), MaxWidth)); + return IntRange::meet( + GetExprRange(C, BO->getLHS(), MaxWidth, IsConstantContext), + GetExprRange(C, BO->getRHS(), MaxWidth, IsConstantContext)); // Left shift gets black-listed based on a judgement call. case BO_Shl: @@ -10022,7 +10026,7 @@ // Right shift by a constant can narrow its left argument. case BO_Shr: case BO_ShrAssign: { - IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth); + IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth, IsConstantContext); // If the shift amount is a positive constant, drop the width by // that much. @@ -10041,7 +10045,7 @@ // Comma acts as its right operand. case BO_Comma: - return GetExprRange(C, BO->getRHS(), MaxWidth); + return GetExprRange(C, BO->getRHS(), MaxWidth, IsConstantContext); // Black-list pointer subtractions. case BO_Sub: @@ -10054,7 +10058,7 @@ case BO_Div: { // Don't 'pre-truncate' the operands. unsigned opWidth = C.getIntWidth(GetExprType(E)); - IntRange L = GetExprRange(C, BO->getLHS(), opWidth); + IntRange L = GetExprRange(C, BO->getLHS(), opWidth, IsConstantContext); // If the divisor is constant, use that. llvm::APSInt divisor; @@ -10068,7 +10072,7 @@ } // Otherwise, just use the LHS's width. - IntRange R = GetExprRange(C, BO->getRHS(), opWidth); + IntRange R = GetExprRange(C, BO->getRHS(), opWidth, IsConstantContext); return IntRange(L.Width, L.NonNegative && R.NonNegative); } @@ -10077,8 +10081,8 @@ case BO_Rem: { // Don't 'pre-truncate' the operands. unsigned opWidth = C.getIntWidth(GetExprType(E)); - IntRange L = GetExprRange(C, BO->getLHS(), opWidth); - IntRange R = GetExprRange(C, BO->getRHS(), opWidth); + IntRange L = GetExprRange(C, BO->getLHS(), opWidth, IsConstantContext); + IntRange R = GetExprRange(C, BO->getRHS(), opWidth, IsConstantContext); IntRange meet = IntRange::meet(L, R); meet.Width = std::min(meet.Width, MaxWidth); @@ -10095,8 +10099,8 @@ // The default case is to treat the operation as if it were closed // on the narrowest type that encompasses both operands. - IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth); - IntRange R = GetExprRange(C, BO->getRHS(), MaxWidth); + IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth, IsConstantContext); + IntRange R = GetExprRange(C, BO->getRHS(), MaxWidth, IsConstantContext); return IntRange::join(L, R); } @@ -10112,12 +10116,12 @@ return IntRange::forValueOfType(C, GetExprType(E)); default: - return GetExprRange(C, UO->getSubExpr(), MaxWidth); + return GetExprRange(C, UO->getSubExpr(), MaxWidth, IsConstantContext); } } if (const auto *OVE = dyn_cast(E)) - return GetExprRange(C, OVE->getSourceExpr(), MaxWidth); + return GetExprRange(C, OVE->getSourceExpr(), MaxWidth, IsConstantContext); if (const auto *BitField = E->getSourceBitField()) return IntRange(BitField->getBitWidthValue(C), @@ -10126,8 +10130,9 @@ return IntRange::forValueOfType(C, GetExprType(E)); } -static IntRange GetExprRange(ASTContext &C, const Expr *E) { - return GetExprRange(C, E, C.getIntWidth(GetExprType(E))); +static IntRange GetExprRange(ASTContext &C, const Expr *E, + bool IsConstantContext) { + return GetExprRange(C, E, C.getIntWidth(GetExprType(E)), IsConstantContext); } /// Checks whether the given value, which currently has the given @@ -10463,6 +10468,8 @@ Expr *LHS = E->getLHS(); Expr *RHS = E->getRHS(); + bool IsConstantContext = S.ExprEvalContexts.back().isConstantEvaluated(); + if (T->isIntegralType(S.Context)) { llvm::APSInt RHSValue; llvm::APSInt LHSValue; @@ -10525,7 +10532,8 @@ } // Otherwise, calculate the effective range of the signed operand. - IntRange signedRange = GetExprRange(S.Context, signedOperand); + IntRange signedRange = + GetExprRange(S.Context, signedOperand, IsConstantContext); // Go ahead and analyze implicit conversions in the operands. Note // that we skip the implicit conversions on both sides. @@ -10542,7 +10550,8 @@ // change the result of the comparison. if (E->isEqualityOp()) { unsigned comparisonWidth = S.Context.getIntWidth(T); - IntRange unsignedRange = GetExprRange(S.Context, unsignedOperand); + IntRange unsignedRange = + GetExprRange(S.Context, unsignedOperand, IsConstantContext); // We should never be unable to prove that the unsigned operand is // non-negative. @@ -11328,7 +11337,8 @@ if (Target->isSpecificBuiltinType(BuiltinType::Bool)) return; - IntRange SourceRange = GetExprRange(S.Context, E); + IntRange SourceRange = GetExprRange( + S.Context, E, S.ExprEvalContexts.back().isConstantEvaluated()); IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target); if (SourceRange.Width > TargetRange.Width) { Index: clang/lib/Sema/SemaCoroutine.cpp =================================================================== --- clang/lib/Sema/SemaCoroutine.cpp +++ clang/lib/Sema/SemaCoroutine.cpp @@ -210,6 +210,7 @@ DiagConstexpr, DiagAutoRet, DiagVarargs, + DiagConsteval }; bool Diagnosed = false; auto DiagInvalid = [&](InvalidFuncDiag ID) { @@ -245,6 +246,8 @@ // [...] an await-expression [...] a yield-expression." if (FD->isConstexpr()) DiagInvalid(DiagConstexpr); + if (FD->isConsteval()) + DiagInvalid(DiagConsteval); // [dcl.spec.auto]p15: "A function declared with a return type that uses a // placeholder type shall not be a coroutine." if (FD->getReturnType()->isUndeducedType()) Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -7991,6 +7991,7 @@ ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier(); bool isConstexpr = D.getDeclSpec().isConstexprSpecified(); + bool isConsteval = D.getDeclSpec().isConstevalSpecified(); // Check that the return type is not an abstract class type. // For record types, this is done by the AbstractClassUsageDiagnoser once @@ -8010,7 +8011,7 @@ return CXXConstructorDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, ExplicitSpecifier, isInline, - /*isImplicitlyDeclared=*/false, isConstexpr); + /*isImplicitlyDeclared=*/false, isConstexpr, isConsteval); } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. @@ -8037,10 +8038,10 @@ // Create a FunctionDecl to satisfy the function definition parsing // code path. - return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), - D.getIdentifierLoc(), Name, R, TInfo, SC, - isInline, - /*hasPrototype=*/true, isConstexpr); + return FunctionDecl::Create( + SemaRef.Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), Name, R, + TInfo, SC, isInline, + /*hasPrototype=*/true, isConstexpr, isConsteval); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -8052,9 +8053,10 @@ SemaRef.CheckConversionDeclarator(D, R, SC); IsVirtualOkay = true; - return CXXConversionDecl::Create( - SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, isInline, ExplicitSpecifier, isConstexpr, SourceLocation()); + return CXXConversionDecl::Create(SemaRef.Context, cast(DC), + D.getBeginLoc(), NameInfo, R, TInfo, + isInline, ExplicitSpecifier, isConstexpr, + isConsteval, SourceLocation()); } else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) { SemaRef.CheckDeductionGuideDeclarator(D, R, SC); @@ -8078,7 +8080,7 @@ // This is a C++ method declaration. CXXMethodDecl *Ret = CXXMethodDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, - TInfo, SC, isInline, isConstexpr, SourceLocation()); + TInfo, SC, isInline, isConstexpr, isConsteval, SourceLocation()); IsVirtualOkay = !Ret->isStatic(); return Ret; } else { @@ -8092,7 +8094,7 @@ // - we're in C++ (where every function has a prototype), return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC, isInline, true /*HasPrototype*/, - isConstexpr); + isConstexpr, isConsteval); } } @@ -8423,6 +8425,7 @@ bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier(); bool isConstexpr = D.getDeclSpec().isConstexprSpecified(); + bool isConsteval = D.getDeclSpec().isConstevalSpecified(); isFriend = D.getDeclSpec().isFriendSpecified(); if (isFriend && !isInline && D.isFunctionDefinition()) { // C++ [class.friend]p5 @@ -8621,16 +8624,40 @@ } } - if (isConstexpr) { - // C++11 [dcl.constexpr]p2: constexpr functions and constexpr constructors - // are implicitly inline. + if (isConstexpr || isConsteval) { + // C++2a [dcl.constexpr]p1: A function or static data member declared with + // the constexpr or consteval specifier is implicitly an inline function + // or variable ([dcl.inline]) NewFD->setImplicitlyInline(); // C++11 [dcl.constexpr]p3: functions declared constexpr are required to // be either constructors or to return a literal type. Therefore, // destructors cannot be declared constexpr. - if (isa(NewFD)) - Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor); + // C++2a [dcl.constexpr]p2: A destructor, an allocation function, or a + // deallocation function shall not be declared with the consteval + // specifier. + if (isa(NewFD)) { + if (isConstexpr) + Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) + << "constexpr"; + if (isConsteval) + Diag(D.getDeclSpec().getConstevalSpecLoc(), diag::err_constexpr_dtor) + << "consteval"; + } + if (isConsteval) { + 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().getConstevalSpecLoc(), + diag::err_invalid_consteval_decl_kind) + << MD->getOverloadedOperator() - OO_New; + NewFD->setConsteval(false); + NewFD->setConstexpr(true); + } + } + } } // If __module_private__ was specified, mark the function accordingly. @@ -9527,6 +9554,7 @@ DeletedFuncs = 5, DefaultedFuncs = 6, ConstexprFuncs = 7, + ConstevalFuncs = 8, }; enum Different { CallingConv = 0, @@ -9534,7 +9562,8 @@ ConstexprSpec = 2, InlineSpec = 3, StorageClass = 4, - Linkage = 5 + Linkage = 5, + ConstevalSpec = 6 }; bool IsCPUSpecificCPUDispatchMVType = @@ -9599,10 +9628,15 @@ return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) << IsCPUSpecificCPUDispatchMVType << DefaultedFuncs; - if (NewFD->isConstexpr() && (MVType == MultiVersionKind::CPUDispatch || - MVType == MultiVersionKind::CPUSpecific)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) - << IsCPUSpecificCPUDispatchMVType << ConstexprFuncs; + if (MVType == MultiVersionKind::CPUDispatch || + MVType == MultiVersionKind::CPUSpecific) { + if (NewFD->isConstexpr()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << IsCPUSpecificCPUDispatchMVType << ConstexprFuncs; + if (NewFD->isConsteval()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_doesnt_support) + << IsCPUSpecificCPUDispatchMVType << ConstevalFuncs; + } QualType NewQType = S.getASTContext().getCanonicalType(NewFD->getType()); const auto *NewType = cast(NewQType); @@ -9637,6 +9671,10 @@ return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << ConstexprSpec; + if (OldFD->isConsteval() != NewFD->isConsteval()) + return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) + << ConstevalSpec; + if (OldFD->isInlineSpecified() != NewFD->isInlineSpecified()) return S.Diag(NewFD->getLocation(), diag::err_multiversion_diff) << InlineSpec; @@ -10383,9 +10421,14 @@ } if (FD->isConstexpr()) { Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_main) - << FixItHint::CreateRemoval(DS.getConstexprSpecLoc()); + << 0 << FixItHint::CreateRemoval(DS.getConstexprSpecLoc()); FD->setConstexpr(false); } + if (FD->isConsteval()) { + Diag(DS.getConstevalSpecLoc(), diag::err_constexpr_main) + << 1 << FixItHint::CreateRemoval(DS.getConstevalSpecLoc()); + FD->setConsteval(false); + } if (getLangOpts().OpenCL) { Diag(FD->getLocation(), diag::err_opencl_no_main) @@ -12972,7 +13015,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()) { @@ -13157,7 +13202,7 @@ // We cannot skip the body of a function with an undeduced return type, // because any callers of that function need to know the type. if (const FunctionDecl *FD = D->getAsFunction()) { - if (FD->isConstexpr()) + if (FD->isConstexprOrConsteval()) return false; // We can't simply call Type::isUndeducedType here, because inside template // auto can be deduced to a dependent type, which is not considered @@ -13486,7 +13531,8 @@ ActivePolicy = &WP; } - if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() && + if (!IsInstantiation && FD && FD->isConstexprOrConsteval() && + !FD->isInvalidDecl() && (!CheckConstexprFunctionDecl(FD) || !CheckConstexprFunctionBody(FD, Body))) FD->setInvalidDecl(); Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -634,12 +634,17 @@ } const FunctionDecl *Def; - // C++11 [dcl.constexpr]p1: If any declaration of a function or function - // template has a constexpr specifier then all its declarations shall - // contain the constexpr specifier. + // C++2a [dcl.constexpr]p1: If any declaration of a function or function + // template has a constexpr or consteval specifier, then all its declarations + // shall contain the same specifier. if (New->isConstexpr() != Old->isConstexpr()) { Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch) - << New << New->isConstexpr(); + << New << New->isConstexpr() << "constexpr"; + Diag(Old->getLocation(), diag::note_previous_declaration); + Invalid = true; + } else if (New->isConsteval() != Old->isConsteval()) { + Diag(New->getLocation(), diag::err_constexpr_redecl_mismatch) + << New << New->isConsteval() << "consteval"; Diag(Old->getLocation(), diag::note_previous_declaration); Invalid = true; } else if (!Old->getMostRecentDecl()->isInlined() && New->isInlined() && @@ -1960,7 +1965,7 @@ !getLangOpts().CPlusPlus2a ? diag::ext_constexpr_function_try_block_cxx2a : diag::warn_cxx17_compat_constexpr_function_try_block) - << isa(Dcl); + << isa(Dcl) << Dcl->isConsteval(); } // - its function-body shall be [...] a compound-statement that contains only @@ -6332,7 +6337,7 @@ auto BaseCtor = Inherited->findConstructorForBase(ClassDecl, InheritedCtor).first; if (BaseCtor) - return BaseCtor->isConstexpr(); + return BaseCtor->isConstexprOrConsteval(); } if (CSM == Sema::CXXDefaultConstructor) @@ -6344,7 +6349,7 @@ // A constructor we wouldn't select can't be "involved in initializing" // anything. return true; - return SMOR.getMethod()->isConstexpr(); + return SMOR.getMethod()->isConstexprOrConsteval(); } /// Determine whether the specified special member function would be constexpr @@ -6631,20 +6636,21 @@ HadError = true; } - // C++11 [dcl.fct.def.default]p2: - // An explicitly-defaulted function may be declared constexpr only if it - // would have been implicitly declared as constexpr, - // Do not apply this rule to members of class templates, since core issue 1358 - // makes such functions always instantiate to constexpr functions. For - // functions which cannot be constexpr (for non-constructors in C++11 and for - // destructors in C++1y), this is checked elsewhere. - // + // C++2a [dcl.fct.def.default]p3: + // An explicitly-defaulted function that is not defined as deleted may be + // declared constexpr or consteval only if it would have been implicitly + // declared as constexpr. + // Do not apply this rule to members of class + // templates, since core issue 1358 makes such functions always instantiate to + // constexpr functions. For functions which cannot be constexpr (for + // non-constructors in C++11 and for destructors in C++1y), this is checked + // elsewhere. // FIXME: This should not apply if the member is deleted. bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, HasConstParam); if ((getLangOpts().CPlusPlus14 ? !isa(MD) : isa(MD)) && - MD->isConstexpr() && !Constexpr && + MD->isConstexprOrConsteval() && !Constexpr && MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { Diag(MD->getBeginLoc(), diag::err_incorrect_defaulted_constexpr) << CSM; // FIXME: Explain why the special member can't be constexpr. @@ -10993,7 +10999,8 @@ CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create( Context, ClassDecl, ClassLoc, NameInfo, /*Type*/ QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), - /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr); + /*isInline=*/true, /*isImplicitlyDeclared=*/true, Constexpr, + /*isConsteval=*/false); DefaultCon->setAccess(AS_public); DefaultCon->setDefaulted(); @@ -11114,7 +11121,7 @@ Context, Derived, UsingLoc, NameInfo, TInfo->getType(), TInfo, BaseCtor->getExplicitSpecifier(), /*Inline=*/true, /*ImplicitlyDeclared=*/true, Constexpr, - InheritedConstructor(Shadow, BaseCtor)); + /*isConsteval=*/false, InheritedConstructor(Shadow, BaseCtor)); if (Shadow->isInvalidDecl()) DerivedCtor->setInvalidDecl(); @@ -11862,10 +11869,10 @@ DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); - CXXMethodDecl *CopyAssignment = - CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), - /*TInfo=*/nullptr, /*StorageClass=*/SC_None, - /*isInline=*/true, Constexpr, SourceLocation()); + CXXMethodDecl *CopyAssignment = CXXMethodDecl::Create( + Context, ClassDecl, ClassLoc, NameInfo, QualType(), + /*TInfo=*/nullptr, /*StorageClass=*/SC_None, + /*isInline=*/true, Constexpr, /*isConsteval=*/false, SourceLocation()); CopyAssignment->setAccess(AS_public); CopyAssignment->setDefaulted(); CopyAssignment->setImplicit(); @@ -12182,10 +12189,10 @@ DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationNameInfo NameInfo(Name, ClassLoc); - CXXMethodDecl *MoveAssignment = - CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), - /*TInfo=*/nullptr, /*StorageClass=*/SC_None, - /*isInline=*/true, Constexpr, SourceLocation()); + CXXMethodDecl *MoveAssignment = CXXMethodDecl::Create( + Context, ClassDecl, ClassLoc, NameInfo, QualType(), + /*TInfo=*/nullptr, /*StorageClass=*/SC_None, + /*isInline=*/true, Constexpr, /*isConsteval=*/false, SourceLocation()); MoveAssignment->setAccess(AS_public); MoveAssignment->setDefaulted(); MoveAssignment->setImplicit(); @@ -12566,7 +12573,8 @@ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), /*isInline=*/true, - /*isImplicitlyDeclared=*/true, Constexpr); + /*isImplicitlyDeclared=*/true, Constexpr, + /*isConsteval=*/false); CopyConstructor->setAccess(AS_public); CopyConstructor->setDefaulted(); @@ -12697,7 +12705,7 @@ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), /*isInline=*/true, - /*isImplicitlyDeclared=*/true, Constexpr); + /*isImplicitlyDeclared=*/true, Constexpr, /*isConsteval=*/false); MoveConstructor->setAccess(AS_public); MoveConstructor->setDefaulted(); @@ -13005,6 +13013,9 @@ if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor)) return ExprError(); + if (CheckInvalidConstevalCall(Constructor, ConstructLoc, ExprArgs)) + return ExprError(); + return CXXConstructExpr::Create( Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs, HadMultipleCandidates, IsListInitialization, Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -454,8 +454,8 @@ if (!checkAddressOfFunctionIsAvailable(FD, Diagnose, E->getExprLoc())) return ExprError(); - E = ImpCastExprToType(E, Context.getPointerType(Ty), - CK_FunctionToPointerDecay).get(); + return ImpCastExprToType(E, Context.getPointerType(Ty), + CK_FunctionToPointerDecay); } else if (Ty->isArrayType()) { // In C90 mode, arrays only promote to pointers if the array expression is // an lvalue. The relevant legalese is C90 6.2.2.1p3: "an lvalue that has @@ -469,8 +469,8 @@ // T" can be converted to an rvalue of type "pointer to T". // if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) - E = ImpCastExprToType(E, Context.getArrayDecayedType(Ty), - CK_ArrayToPointerDecay).get(); + return ImpCastExprToType(E, Context.getArrayDecayedType(Ty), + CK_ArrayToPointerDecay); } return E; } @@ -666,7 +666,10 @@ // to function type. if (Ty->isFunctionType()) { Res = ImpCastExprToType(E, Context.getPointerType(Ty), - CK_FunctionToPointerDecay).get(); + CK_FunctionToPointerDecay, VK_RValue, nullptr, + CCK_ImplicitConversion, + /*isCastForCall*/ true) + .get(); if (Res.isInvalid()) return ExprError(); } @@ -5714,6 +5717,57 @@ return SemaConvertVectorExpr(E, TInfo, BuiltinLoc, RParenLoc); } +bool Sema::CheckInvalidConstevalCall(FunctionDecl *FDecl, SourceLocation Loc, + ArrayRef Args, Expr *This) { + bool Failed = false; + if (FDecl && FDecl->isConsteval() && + !ExprEvalContexts.back().isConstantEvaluated()) { + + SmallVector Diags; + auto Check = [&](Expr *Arg, unsigned int DiagID, int Index = 0) { + Expr::EvalResult Result; + Result.Diag = &Diags; + if (!Arg->EvaluateAsRValue(Result, getASTContext(), true)) { + if (!Failed) { + Failed = true; + const char *DeclName = + (isa(FDecl) ? "consteval constructor" + : "consteval function"); + if (FDecl->isOverloadedOperator()) + DeclName = "consteval operator"; + Diag(Loc, diag::err_consteval_cannot_be_constant_eval) << DeclName; + Diag(FDecl->getLocation(), diag::note_callee_decl) << DeclName; + } + Diag(Arg->getBeginLoc(), DiagID) << Index; + for (const auto &Diag : Diags) { + this->Diag(Diag.first, Diag.second); + } + Diags.clear(); + } + }; + if (This) { + This = This->IgnoreParens(); + if (auto *MemExpr = dyn_cast(This)) { + This = nullptr; + if (MemExpr->children().begin() != MemExpr->children().end()) + if (*MemExpr->children().begin()) + This = cast(*MemExpr->children().begin()); + } + + if (This) { + This = This->IgnoreParenCasts(); + Check(This, diag::note_not_constexpr_this); + } + } + for (auto It = Args.begin(); It != Args.end(); It++) { + Expr *Arg = *It; + Check(Arg, diag::note_argument_n_cannot_be_constant_eval, + It - Args.begin()); + } + } + return Failed; +} + /// BuildResolvedCallExpr - Build a call to a resolved expression, /// i.e. an expression not of \p OverloadTy. The expression should /// unary-convert to an expression of function-pointer or @@ -5745,6 +5799,9 @@ Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention); } + if (CheckInvalidConstevalCall(FDecl, LParenLoc, Args)) + return ExprError(); + // Promote the function operand. // We special-case function promotion here because we only allow promoting // builtin functions to function pointers in the callee of a call. @@ -13357,10 +13414,33 @@ return CreateBuiltinUnaryOp(OpLoc, Opc, Input); } +bool Sema::CheckInvalidConstevalTakeAddress(Expr *Input) { + if (Input) { + if (auto *DeclRef = dyn_cast(Input->IgnoreParens())) { + if (auto *Function = dyn_cast(DeclRef->getDecl())) { + if (Function->isConsteval()) { + const char *DeclName = Function->isOverloadedOperator() + ? "consteval operator" + : "consteval function"; + Diag(DeclRef->getExprLoc(), diag::err_take_adress_of_consteval_decl) + << DeclName; + Diag(Function->getLocation(), diag::note_entity_declared_at) + << DeclName; + return true; + } + } + } + } + return false; +} + // Unary Operators. 'Tok' is the token for the operator. ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, Expr *Input) { - return BuildUnaryOp(S, OpLoc, ConvertTokenKindToUnaryOpcode(Op), Input); + UnaryOperatorKind Opc = ConvertTokenKindToUnaryOpcode(Op); + if (Opc == UO_AddrOf && CheckInvalidConstevalTakeAddress(Input)) + return ExprError(); + return BuildUnaryOp(S, OpLoc, Opc, Input); } /// ActOnAddrLabel - Parse the GNU address of label extension: "&&foo". @@ -14782,7 +14862,7 @@ static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { CXXMethodDecl *MD = dyn_cast(Func); - return Func->isConstexpr() && + return Func->isConstexprOrConsteval() && (Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided())); } @@ -14918,13 +14998,13 @@ } if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || - Func->isConstexpr()) { + Func->isConstexprOrConsteval()) { if (isa(Func->getDeclContext()) && cast(Func->getDeclContext())->isLocalClass() && CodeSynthesisContexts.size()) PendingLocalImplicitInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) + else if (Func->isConstexprOrConsteval()) // Do not defer instantiations of constexpr functions, to avoid the // expression evaluator needing to call back into Sema if it sees a // call to such a function. Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -4012,6 +4012,9 @@ llvm_unreachable("Improper first standard conversion"); } + if (!From) + return ExprError(); + // Perform the second implicit conversion switch (SCS.Second) { case ICK_Identity: Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -372,7 +372,8 @@ TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc, ArrayRef Params, - const bool IsConstexprSpecified) { + const bool IsConstexprSpecified, + const bool IsConstevalSpecified) { QualType MethodType = MethodTypeInfo->getType(); TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -401,16 +402,12 @@ = IntroducerRange.getBegin().getRawEncoding(); MethodNameLoc.CXXOperatorName.EndOpNameLoc = IntroducerRange.getEnd().getRawEncoding(); - CXXMethodDecl *Method - = CXXMethodDecl::Create(Context, Class, EndLoc, - DeclarationNameInfo(MethodName, - IntroducerRange.getBegin(), - MethodNameLoc), - MethodType, MethodTypeInfo, - SC_None, - /*isInline=*/true, - IsConstexprSpecified, - EndLoc); + CXXMethodDecl *Method = CXXMethodDecl::Create( + Context, Class, EndLoc, + DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), + MethodNameLoc), + MethodType, MethodTypeInfo, SC_None, + /*isInline=*/true, IsConstexprSpecified, IsConstevalSpecified, EndLoc); Method->setAccess(AS_public); // Temporarily set the lexical declaration context to the current @@ -929,7 +926,8 @@ CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, - ParamInfo.getDeclSpec().isConstexprSpecified()); + ParamInfo.getDeclSpec().isConstexprSpecified(), + ParamInfo.getDeclSpec().isConstevalSpecified()); if (ExplicitParams) CheckCXXDefaultArguments(Method); @@ -1181,7 +1179,9 @@ // Enter a new evaluation context to insulate the lambda from any // cleanups from the enclosing full-expression. PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated); + Method->isConsteval() + ? ExpressionEvaluationContext::ConstantEvaluated + : ExpressionEvaluationContext::PotentiallyEvaluated); } void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope, @@ -1327,7 +1327,7 @@ DeclarationNameInfo(ConversionName, Loc, ConvNameLoc), ConvTy, ConvTSI, /*isInline=*/true, ExplicitSpecifier(), /*isConstexpr=*/S.getLangOpts().CPlusPlus17, - CallOperator->getBody()->getEndLoc()); + /*isConsteval=*/false, CallOperator->getBody()->getEndLoc()); Conversion->setAccess(AS_public); Conversion->setImplicit(true); @@ -1366,7 +1366,8 @@ S.Context, Class, Loc, DeclarationNameInfo(InvokerName, Loc), InvokerFunctionTy, CallOperator->getTypeSourceInfo(), SC_Static, /*IsInline=*/true, - /*IsConstexpr=*/false, CallOperator->getBody()->getEndLoc()); + /*IsConstexpr=*/false, + /*IsConsteval=*/false, CallOperator->getBody()->getEndLoc()); for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) InvokerParams[I]->setOwningFunction(Invoke); Invoke->setParams(InvokerParams); @@ -1413,7 +1414,8 @@ S.Context, Class, Loc, DeclarationNameInfo(Name, Loc, NameLoc), ConvTy, S.Context.getTrivialTypeSourceInfo(ConvTy, Loc), /*isInline=*/true, ExplicitSpecifier(), - /*isConstexpr=*/false, CallOperator->getBody()->getEndLoc()); + /*isConstexpr=*/false, /*isConsteval=*/false, + CallOperator->getBody()->getEndLoc()); Conversion->setAccess(AS_public); Conversion->setImplicit(true); Class->addDecl(Conversion); @@ -1681,7 +1683,7 @@ // and we are not in a dependent context, analyze the call operator to infer // its constexpr-ness, suppressing diagnostics while doing so. if (getLangOpts().CPlusPlus17 && !CallOperator->isInvalidDecl() && - !CallOperator->isConstexpr() && + !CallOperator->isConstexprOrConsteval() && !isa(CallOperator->getBody()) && !Class->getDeclContext()->isDependentContext()) { TentativeAnalysisScope DiagnosticScopeGuard(*this); Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -69,7 +69,9 @@ S.MarkDeclRefReferenced(DRE, Base); return S.ImpCastExprToType(DRE, S.Context.getPointerType(DRE->getType()), - CK_FunctionToPointerDecay); + CK_FunctionToPointerDecay, VK_RValue, nullptr, + Sema::CCK_ImplicitConversion, + /*isCastForCall*/ true); } static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, @@ -13110,6 +13112,9 @@ MemExpr = cast(MemExprE->IgnoreParens()); } + if (CheckInvalidConstevalCall(Method, LParenLoc, Args, MemExprE)) + return ExprError(); + QualType ResultType = Method->getReturnType(); ExprValueKind VK = Expr::getValueKindForType(ResultType); ResultType = ResultType.getNonLValueExprType(Context); @@ -13367,6 +13372,9 @@ CheckMemberOperatorAccess(LParenLoc, Object.get(), nullptr, Best->FoundDecl); + if (CheckInvalidConstevalCall(Best->Function, LParenLoc, Args, Object.get())) + return ExprError(); + // We found an overloaded operator(). Build a CXXOperatorCallExpr // that calls this method, using Object for the implicit object // parameter and passing along the remaining arguments. Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -8255,7 +8255,7 @@ // it will be a static member function until we know which template it // specializes), so adjust it now assuming it specializes this template. QualType FT = FD->getType(); - if (FD->isConstexpr()) { + if (FD->isConstexprOrConsteval()) { CXXMethodDecl *OldMD = dyn_cast(FunTmpl->getTemplatedDecl()); if (OldMD && OldMD->isConst()) { @@ -8362,6 +8362,7 @@ // FIXME: What if there are multiple such prior declarations (for instance, // from different modules)? Specialization->setConstexpr(FD->isConstexpr()); + Specialization->setConsteval(FD->isConsteval()); } // FIXME: Check if the prior specialization has a point of instantiation. Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1768,7 +1768,7 @@ Function = FunctionDecl::Create( SemaRef.Context, DC, D->getInnerLocStart(), NameInfo, T, TInfo, D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(), - D->hasWrittenPrototype(), D->isConstexpr()); + D->hasWrittenPrototype(), D->isConstexpr(), D->isConsteval()); Function->setRangeEnd(D->getSourceRange().getEnd()); } @@ -2076,7 +2076,7 @@ Method = CXXConstructorDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, InstantiatedExplicitSpecifier, Constructor->isInlineSpecified(), false, - Constructor->isConstexpr()); + Constructor->isConstexpr(), Constructor->isConsteval()); Method->setRangeEnd(Constructor->getEndLoc()); } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, @@ -2088,12 +2088,14 @@ Method = CXXConversionDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, Conversion->isInlineSpecified(), InstantiatedExplicitSpecifier, - Conversion->isConstexpr(), Conversion->getEndLoc()); + Conversion->isConstexpr(), Conversion->isConsteval(), + Conversion->getEndLoc()); } else { StorageClass SC = D->isStatic() ? SC_Static : SC_None; Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, SC, D->isInlineSpecified(), - D->isConstexpr(), D->getEndLoc()); + D->isConstexpr(), D->isConsteval(), + D->getEndLoc()); } if (D->isInlined()) Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -11224,7 +11224,7 @@ Class, E->getIntroducerRange(), NewCallOpTSI, E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs().getParams(), - E->getCallOperator()->isConstexpr()); + E->getCallOperator()->isConstexpr(), E->getCallOperator()->isConsteval()); LSI->CallOperator = NewCallOperator; Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -869,6 +869,7 @@ FD->setExplicitlyDefaulted(Record.readInt()); FD->setHasImplicitReturnZero(Record.readInt()); FD->setConstexpr(Record.readInt()); + FD->setConsteval(Record.readInt()); FD->setUsesSEHTry(Record.readInt()); FD->setHasSkippedBody(Record.readInt()); FD->setIsMultiVersion(Record.readInt()); Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -546,6 +546,7 @@ Record.push_back(D->isExplicitlyDefaulted()); Record.push_back(D->hasImplicitReturnZero()); Record.push_back(D->isConstexpr()); + Record.push_back(D->isConsteval()); Record.push_back(D->usesSEHTry()); Record.push_back(D->hasSkippedBody()); Record.push_back(D->isMultiVersion()); @@ -2174,6 +2175,7 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ExplicitlyDefaulted Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ImplicitReturnZero Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Constexpr + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Consteval Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // UsesSEHTry Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion Index: clang/test/CodeGenCXX/cxx2a-consteval.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/cxx2a-consteval.cpp @@ -0,0 +1,172 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -std=c++2a -o %t.ll +// RUN: FileCheck -check-prefix=CHECK -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-FN -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-STATIC -input-file=%t.ll %s + +// CHECK-NOT: @__cxx_global_var_init() + +//CHECK-NOT: @_Z4ret7v() +consteval int ret7() { + return 7; +} + +//CHECK-FN: define i32 @_Z9test_ret7v() +int test_ret7() { + //CHECK-FN: store i32 7, i32* %i, align 4 + //CHECK-FN: ret i32 %{{[0-9]+}} + int i = ret7(); + return i; +} + +//CHECK-STATIC: @global_i = global i32 7, align 4 +int global_i = ret7(); + +// CHECK-STATIC: @_ZL7i_const = internal constant i32 5, align 4 +constexpr int i_const = 5; + +//CHECK-NOT: @_Z4retIv() +consteval const int& retI() { + return i_const; +} + +//CHECK-FN: define i32 @_Z9test_retIv() +int test_retI() { +//CHECK-FN: %{{[0-9]+}} = load i32, i32* @_ZL7i_const, align 4 +//CHECK-FN: ret i32 %{{[0-9]+}} + return retI(); +} + +//CHECK-NOT: @_Z4retIv() +consteval const int* retIPtr() { + return &i_const; +} + +//CHECK-FN: define i32 @_Z12test_retIPtrv() +int test_retIPtr() { +//CHECK-FN: %{{[0-9]+}} = load i32, i32* @_ZL7i_const, align 4 +//CHECK-FN: ret i32 %{{[0-9]+}} + return *retIPtr(); +} + +//CHECK-NOT: @_Z4retIv() +consteval const int&& retIRRef() { + return static_cast(i_const); +} + +//CHECK-FN: define i32 @_Z13test_retIRRefv() +int test_retIRRef() { +//CHECK-FN: %{{[0-9]+}} = load i32, i32* @_ZL7i_const, align 4 +//CHECK-FN: ret i32 %{{[0-9]+}} + return retIRRef(); +} + +//CHECK-NOT: @_Z13retRefInvalidv() +consteval const int& retRefInvalid() { + int i = 11; + return i; +} + +//CHECK-FN: define i32 @_Z18test_retRefInvalidv() +int test_retRefInvalid() { + const int& ref = retRefInvalid(); + //CHECK-FN: store i32* null, i32** %ref, align 8 + //CHECK-FN: ret i32 %{{[0-9]+}} + return ref; +} + +//CHECK-NOT: @_Z13retPtrInvalidv() +consteval const int* retPtrInvalid() { + int i = 11; + return &i; +} + +//CHECK-FN: define i32 @_Z18test_retPtrInvalidv() +int test_retPtrInvalid() { + const int* ref = retPtrInvalid(); + //CHECK-FN: store i32* null, i32** %ref, align 8 + //CHECK-FN: ret i32 %{{[0-9]+}} + return *ref; +} + +//CHECK-NOT: @_Z14retRRefInvalidv() +consteval const int&& retRRefInvalid() { + int i = 11; + return static_cast(i); +} + +//CHECK-FN: define i32 @_Z19test_retRRefInvalidv() +int test_retRRefInvalid() { + const int& ref = retRRefInvalid(); + //CHECK-FN: store i32* null, i32** %ref, align 8 + //CHECK-FN: ret i32 %{{[0-9]+}} + return ref; +} + +struct Agg { + int a; + long b; +}; + +//CHECK-NOT: @_Z6retAggv() +consteval Agg retAgg() { + return {13, 17}; +} + +//CHECK-FN: define i64 @_Z11test_retAggv() +long test_retAgg() { +//CHECK-FN: store i32 13, i32* %{{[0-9]+}}, align 8 +//CHECK-FN: store i64 17, i64* %{{[0-9]+}}, align 8 +//CHECK-FN: ret i64 %{{[0-9]+}} + long b = retAgg().b; + return b; +} + +//CHECK-STATIC: @A = global %struct.Agg { i32 13, i64 17 }, align 8 +Agg A = retAgg(); + +//CHECK-NOT: @_Z9retRefAggv() +consteval const Agg& retRefAgg() { + const Agg& tmp = A; + return A; +} + +//CHECK-FN: define i64 @_Z14test_retRefAggv() +long test_retRefAgg() { +//CHECK-FN: store i32 13, i32* %{{[0-9]+}}, align 8 +//CHECK-FN: store i64 17, i64* %{{[0-9]+}}, align 8 +//CHECK-FN: ret i64 %{{[0-9]+}} + long b = retAgg().b; + return b; +} + +//CHECK-NOT: define i64 @_Z7is_constv() +consteval Agg is_const() { + return {5, 19 * __builtin_is_constant_evaluated()}; +} + +//CHECK-FN: define i64 @_Z13test_is_constv() +long test_is_const() { +//CHECK-FN: store i32 5, i32* %{{[0-9]+}}, align 8 +//CHECK-FN: store i64 19, i64* %{{[0-9]+}}, align 8 +//CHECK-FN: ret i64 %{{[0-9]+}} + long b = is_const().b; + return b; +} + +//CHECK-NOT: @_ZN7AggCtorC +struct AggCtor { + consteval AggCtor(int a = 3, long b = 5) : a(a * a), b(a * b) {} + int a; + long b; +}; + +//CHECK-FN: define i64 @_Z12test_AggCtorv() +long test_AggCtor() { +//CHECK-FN: store i32 4, i32* %{{[0-9]+}}, align 8 +//CHECK-FN: store i64 10, i64* %{{[0-9]+}}, align 8 +//CHECK-FN: ret i64 % + const int i = 2; + AggCtor A(i); + return A.a + A.b; +} + Index: clang/test/SemaCXX/cxx2a-compat.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-compat.cpp +++ clang/test/SemaCXX/cxx2a-compat.cpp @@ -56,4 +56,12 @@ #if !defined(__cpp_conditional_explicit) || __cpp_conditional_explicit != 201806L #error "the feature test macro __cpp_conditional_explicit isn't correct" #endif +#endif + +auto l = []() consteval {}; +int consteval(); +#if __cplusplus <= 201703L +// expected-warning@-3 {{'consteval' is a keyword in C++2a}} +#else +// expected-error@-4 {{expected unqualified-id}} #endif \ No newline at end of file Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -0,0 +1,224 @@ +// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify + +int i_global; // expected-note+ {{declared here}} +extern int i_extern; // expected-note+ {{declared here}} +const int i_const = 0; +constexpr int i_constexpr = 0; + +consteval int f_eval(int i) { +// expected-note@-1+ {{consteval function declared here}} + return i; +} + +constexpr auto l_eval = [](int i) consteval { +// expected-note@-1+ {{consteval operator declared here}} + return i; +}; + +constexpr int f_expr(int i) { + return i; +} + +struct A { + consteval int f_eval(int i) const { +// expected-note@-1+ {{consteval function declared here}} + return i; + } +}; + +constexpr A a; + +namespace invalid_call_args { + +int d0 = f_eval(0); +int l0 = l_eval(0); +int m0 = a.f_eval(0); +int d2 = f_eval(i_extern); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} +int l2 = l_eval(i_extern); +// expected-error@-1 {{call to consteval operator cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} +int m2 = a.f_eval(i_extern); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} +int d4 = f_eval(i_const); +int l4 = l_eval(i_const); +int m4 = a.f_eval(i_const); +int d6 = f_eval(i_constexpr); +int l6 = l_eval(i_constexpr); +int m6 = a.f_eval(i_constexpr); +int d8 = f_eval(i_global); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} +int l8 = l_eval(i_global); +// expected-error@-1 {{call to consteval operator cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} +int m8 = a.f_eval(i_global); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{argument 0 cannot be constant evaluated}} +// expected-note@-3 {{not allowed in a constant expression}} + +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 {{call to consteval function cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + // expected-error@-4 {{variables defined in a constexpr function must be initialized}} + // TODO: prevent the error from the previous line from being emitted. + int l2 = l_eval(i); + // expected-error@-1 {{call to consteval operator cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + int m2 = a.f_eval(i); + // expected-error@-1 {{call to consteval function cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + int d4 = f_eval(i_const); + int l4 = l_eval(i_const); + int m4 = a.f_eval(i_const); + int d6 = f_eval(i_constexpr); + int l6 = l_eval(i_constexpr); + int m6 = a.f_eval(i_constexpr); + int d8 = f_eval(i_global); + // expected-error@-1 {{call to consteval function cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + int l8 = l_eval(i_global); + // expected-error@-1 {{call to consteval operator cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + int m8 = a.f_eval(i_global); + // expected-error@-1 {{call to consteval function cannot be constant evaluated}} + // expected-note@-2 {{argument 0 cannot be constant evaluated}} + // expected-note@-3 {{not allowed in a constant expression}} + return 0; +} + +consteval int f2(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int d0 = f_eval(i); + int l0 = l_eval(i); + int m0 = a.f_eval(i); + int d2 = f_eval(i_extern); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} +consteval int f3(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int m2 = a.f_eval(i_extern); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} +consteval int l3(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int l2 = l_eval(i_extern); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} +consteval int f4(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int d4 = f_eval(i_const); + int l4 = l_eval(i_const); + int m4 = a.f_eval(i_const); + int d6 = f_eval(i_constexpr); + int l6 = l_eval(i_constexpr); + int m6 = a.f_eval(i_constexpr); + int d8 = f_eval(i_global); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} +consteval int f5(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int l8 = l_eval(i_global); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} +consteval int m5(int i) { + // expected-error@-1 {{constexpr function never produces a constant expression}} + int m8 = a.f_eval(i_global); + // expected-note@-1 {{not allowed in a constant expression}} + return 0; +} + +consteval int f_multi(int a, int b, int c) { +// expected-note@-1 {{declared here}} + return a + b + c; +} + +int v = f_multi(i_const, i_extern, i_global); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{argument 1 cannot be constant evaluate}} +// expected-note@-3+ {{not allowed in a constant expression}} +// expected-note@-4 {{argument 2 cannot be constant evaluate}} + +constexpr A a; + +A a_dependent; +// expected-note@-1+ {{declared here}} +auto l_dependent = [](int i) consteval { +// expected-note@-1+ {{declared here}} + return i; +}; + +int i_l = l_dependent(0); +// expected-error@-1 {{call to consteval operator cannot be constant evaluated}} +// expected-note@-2 {{call to member of non-constexpr value}} +// expected-note@-3 {{not allowed in a constant expression}} +int i_m = a_dependent.f_eval(0); +// expected-error@-1 {{call to consteval function cannot be constant evaluated}} +// expected-note@-2 {{call to member of non-constexpr value}} +// expected-note@-3 {{not allowed in a constant expression}} +} + +namespace taking_address { + +using func_type = int(int); +using mem_ptr_type = int(A::*)(int); + +func_type* p1 = (f_eval); +// expected-error@-1 {{taking address of a consteval function}} +func_type* p2= &(((f_eval))); +// expected-error@-1 {{taking address of a consteval function}} +func_type* p3 = (func_type*)f_eval; +// expected-error@-1 {{taking address of a consteval function}} +func_type* p4 = static_cast(f_eval); +// expected-error@-1 {{taking address of a consteval function}} +func_type* p5 = reinterpret_cast(f_eval); +// expected-error@-1 {{taking address of a consteval function}} +func_type* p6 = reinterpret_cast(&reinterpret_cast(f_eval)); +// expected-error@-1 {{taking address of a consteval function}} +func_type* p7 = __builtin_addressof(f_eval); +// expected-error@-1 {{taking address of a consteval function}} + +mem_ptr_type m1 = &A::f_eval; +// expected-error@-1 {{taking address of a consteval function}} +auto* l1 = &decltype(l_eval)::operator(); +// expected-error@-1 {{taking address of a consteval operator}} + +} + +namespace invalid_function { +using size_t = unsigned long; +struct A { + consteval void *operator new(size_t count); + // expected-error@-1 {{operator new cannot be marked consteval}} + consteval void *operator new[](size_t count); + // expected-error@-1 {{operator new[] cannot be marked consteval}} + consteval void operator delete(void* ptr); + // expected-error@-1 {{operator delete cannot be marked consteval}} + consteval void operator delete[](void* ptr); + // expected-error@-1 {{operator delete[] cannot be marked consteval}} + consteval ~A(); + // expected-error@-1 {{destructor cannot be marked consteval}} +}; + +}