Index: clang/include/clang/AST/CXXRecordDeclDefinitionBits.def =================================================================== --- clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -180,6 +180,9 @@ /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. FIELD(DeclaredNonTrivialSpecialMembersForCall, 6, MERGE_OR) +/// The special member which have been defaulted with the consteval keyword. +FIELD(DefaultedSpecialMemberIsConsteval, 6, MERGE_OR) + /// True when this class has a destructor with no semantic effect. FIELD(HasIrrelevantDestructor, 1, NO_MERGE) Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -822,6 +822,41 @@ needsImplicitMoveConstructor(); } + /// Determine whether this class has a defaulted consteval default + /// constructor. + bool hasDefaultedConstevalDefaultCtor() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_DefaultConstructor; + } + + /// Determine whether this class has a defaulted consteval copy + /// constructor. + bool hasDefaultedConstevalCopyCtor() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_CopyConstructor; + } + + /// Determine whether this class has a defaulted consteval move + /// constructor. + bool hasDefaultedConstevalMoveCtor() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_MoveConstructor; + } + + /// Determine whether this class has a defaulted consteval copy assignment + /// operator. + bool hasDefaultedConstevalCopyAssignment() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_CopyAssignment; + } + + /// Determine whether this class has a defaulted consteval move assignment + /// operator. + bool hasDefaultedConstevalMoveAssignment() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_MoveAssignment; + } + + /// Determine whether this class has a defaulted consteval destructor. + bool hasDefaultedConstevalDestructor() const { + return data().DefaultedSpecialMemberIsConsteval & SMF_Destructor; + } + /// Set that we attempted to declare an implicit copy /// constructor, but overload resolution failed so we deleted it. void setImplicitCopyConstructorIsDeleted() { Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -687,7 +687,8 @@ /// Evaluate an expression that is required to be a constant expression. bool EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, - const ASTContext &Ctx) const; + const ASTContext &Ctx, + bool InPlace = false) const; /// If the current Expr is a pointer, this will try to statically /// determine the number of bytes available where the pointer is pointing. @@ -997,7 +998,8 @@ static ConstantExpr *Create(const ASTContext &Context, Expr *E, const APValue &Result); static ConstantExpr *Create(const ASTContext &Context, Expr *E, - ResultStorageKind Storage = RSK_None); + ResultStorageKind Storage = RSK_None, + bool IsCausedByImmediateInvocation = false); static ConstantExpr *CreateEmpty(const ASTContext &Context, ResultStorageKind StorageKind, EmptyShell Empty); @@ -1028,6 +1030,9 @@ ResultStorageKind getResultStorageKind() const { return static_cast(ConstantExprBits.ResultKind); } + bool IsCausedByImmediateInvocation() const { + return ConstantExprBits.IsCausedByImmediateInvocation; + } APValue getAPValueResult() const; const APValue &getResultAsAPValue() const { return APValueResult(); } llvm::APSInt getResultAsAPSInt() const; Index: clang/include/clang/AST/Stmt.h =================================================================== --- clang/include/clang/AST/Stmt.h +++ clang/include/clang/AST/Stmt.h @@ -347,6 +347,9 @@ /// When ResultKind == RSK_APValue. Wether the ASTContext will cleanup the /// destructor on the trail-allocated APValue. unsigned HasCleanup : 1; + + /// Whether this ConstantExpr was created for immediate invocation. + unsigned IsCausedByImmediateInvocation : 1; }; class PredefinedExprBitfields { Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -57,6 +57,9 @@ def note_constexpr_dynamic_alloc : Note< "%select{pointer|reference}0 to %select{|subobject of }1" "heap-allocated object is not a constant expression">; +def note_consteval_address_accessible : Note< + "%select{pointer|reference}0 to a consteval declaration " + "is not a constant expression">; def note_constexpr_uninitialized : Note< "%select{|sub}0object of type %1 is not initialized">; def note_constexpr_subobject_declared_here : Note< Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2357,6 +2357,13 @@ "'constexpr' non-static member function will not be implicitly 'const' " "in C++14; add 'const' to avoid a change in behavior">, InGroup>; +def err_invalid_consteval_take_address : Error< + "cannot take address of consteval function %0 outside" + " of an immediate invocation">; +def err_invalid_consteval_call : Error< + "call to consteval function '%0' could not be evaluated">; +def err_invalid_consteval_decl_kind : Error< + "%0 cannot be declared consteval">; def err_invalid_constexpr : Error< "%select{function parameter|typedef}0 " "cannot be %sub{select_constexpr_spec_kind}1">; @@ -2454,7 +2461,7 @@ "is incompatible with C++ standards before C++20">, InGroup, DefaultIgnore; def ext_constexpr_function_never_constant_expr : ExtWarn< - "constexpr %select{function|constructor}0 never produces a " + "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a " "constant expression">, InGroup>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -808,6 +808,11 @@ } }; + /// Whether the AST is currently being rebuild to correct immediate + /// invocations. Immediate invocation candidates and references to consteval + /// functions aren't tracked when this is set. + bool RebuildingImmediateInvocation; + /// Used to change context to isConstantEvaluated without pushing a heavy /// ExpressionEvaluationContextRecord object. bool isConstantEvaluatedOverride; @@ -1019,6 +1024,8 @@ PotentiallyEvaluatedIfUsed }; + using IICandidate = llvm::PointerIntPair; + /// Data structure used to record current or nested /// expression evaluation contexts. struct ExpressionEvaluationContextRecord { @@ -1065,6 +1072,13 @@ /// they are not discarded-value expressions nor unevaluated operands. SmallVector VolatileAssignmentLHSs; + /// Set of candidate for starting an immediate invocation. + llvm::SmallVector ImmediateInvocationCandidates; + + /// Set of DeclRefExpr referencing a consteval function when used in a + /// context not already know to be immediately invoked. + llvm::SmallPtrSet ReferenceToConsteval; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -5421,6 +5435,10 @@ /// it simply returns the passed in expression. ExprResult MaybeBindToTemporary(Expr *E); + /// Wrap the expression in a ConstantExpr if it is a potention immediate + /// invocation + ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl); + bool CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, SourceLocation Loc, Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -90,7 +90,8 @@ DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All), HasTrivialSpecialMembersForCall(SMF_All), DeclaredNonTrivialSpecialMembers(0), - DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true), + DeclaredNonTrivialSpecialMembersForCall(0), + DefaultedSpecialMemberIsConsteval(0), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), HasDefaultedDefaultConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), @@ -755,8 +756,11 @@ data().UserProvidedDefaultConstructor = true; if (Constructor->isConstexpr()) data().HasConstexprDefaultConstructor = true; - if (Constructor->isDefaulted()) + if (Constructor->isDefaulted()) { data().HasDefaultedDefaultConstructor = true; + if (Constructor->isConsteval()) + data().DefaultedSpecialMemberIsConsteval |= SMF_DefaultConstructor; + } } if (!FunTmpl) { @@ -766,8 +770,13 @@ if (Quals & Qualifiers::Const) data().HasDeclaredCopyConstructorWithConstParam = true; - } else if (Constructor->isMoveConstructor()) + if (Constructor->isConsteval() && Constructor->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_CopyConstructor; + } else if (Constructor->isMoveConstructor()) { SMKind |= SMF_MoveConstructor; + if (Constructor->isConsteval() && Constructor->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_MoveConstructor; + } } // C++11 [dcl.init.aggr]p1: DR1518 @@ -798,6 +807,8 @@ if (const auto *DD = dyn_cast(D)) { SMKind |= SMF_Destructor; + if (DD->isConsteval() && DD->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_Destructor; if (DD->isUserProvided()) data().HasIrrelevantDestructor = false; // If the destructor is explicitly defaulted and not trivial or not public @@ -817,15 +828,22 @@ if (Method->isCopyAssignmentOperator()) { SMKind |= SMF_CopyAssignment; + if (Method->isConsteval() && Method->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_CopyAssignment; + const auto *ParamTy = Method->getParamDecl(0)->getType()->getAs(); if (!ParamTy || ParamTy->getPointeeType().isConstQualified()) data().HasDeclaredCopyAssignmentWithConstParam = true; } - if (Method->isMoveAssignmentOperator()) + if (Method->isMoveAssignmentOperator()) { SMKind |= SMF_MoveAssignment; + if (Method->isConsteval() && Method->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_CopyAssignment; + } + // Keep the list of conversion functions up-to-date. if (auto *Conversion = dyn_cast(D)) { // FIXME: We use the 'unsafe' accessor for the access specifier here, Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -277,7 +277,8 @@ } ConstantExpr *ConstantExpr::Create(const ASTContext &Context, Expr *E, - ResultStorageKind StorageKind) { + ResultStorageKind StorageKind, + bool IsCausedByImmediateInvocation) { assert(!isa(E)); AssertResultStorageKind(StorageKind); unsigned Size = totalSizeToAlloc( @@ -285,6 +286,8 @@ StorageKind == ConstantExpr::RSK_Int64); void *Mem = Context.Allocate(Size, alignof(ConstantExpr)); ConstantExpr *Self = new (Mem) ConstantExpr(E, StorageKind); + Self->ConstantExprBits.IsCausedByImmediateInvocation = + IsCausedByImmediateInvocation; return Self; } Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -628,33 +628,31 @@ namespace { /// A cleanup, and a flag indicating whether it is lifetime-extended. - class Cleanup { - llvm::PointerIntPair Value; - APValue::LValueBase Base; - QualType T; +struct Cleanup { + llvm::PointerIntPair Value; + APValue::LValueBase Base; + QualType T; - public: - Cleanup(APValue *Val, APValue::LValueBase Base, QualType T, - bool IsLifetimeExtended) - : Value(Val, IsLifetimeExtended), Base(Base), T(T) {} - - bool isLifetimeExtended() const { return Value.getInt(); } - bool endLifetime(EvalInfo &Info, bool RunDestructors) { - if (RunDestructors) { - SourceLocation Loc; - if (const ValueDecl *VD = Base.dyn_cast()) - Loc = VD->getLocation(); - else if (const Expr *E = Base.dyn_cast()) - Loc = E->getExprLoc(); - return HandleDestruction(Info, Loc, Base, *Value.getPointer(), T); - } - *Value.getPointer() = APValue(); - return true; - } +public: + Cleanup(APValue *Val, APValue::LValueBase Base, QualType T, + bool IsLifetimeExtended) + : Value(Val, IsLifetimeExtended), Base(Base), T(T) {} - bool hasSideEffect() { - return T.isDestructedType(); + bool isLifetimeExtended() const { return Value.getInt(); } + bool endLifetime(EvalInfo &Info, bool RunDestructors) { + if (RunDestructors) { + SourceLocation Loc; + if (const ValueDecl *VD = Base.dyn_cast()) + Loc = VD->getLocation(); + else if (const Expr *E = Base.dyn_cast()) + Loc = E->getExprLoc(); + return HandleDestruction(Info, Loc, Base, *Value.getPointer(), T); } + *Value.getPointer() = APValue(); + return true; + } + + bool hasSideEffect() { return T.isDestructedType(); } }; /// A reference to an object whose construction we are currently evaluating. @@ -1041,6 +1039,7 @@ bool discardCleanups() { for (Cleanup &C : CleanupStack) { if (C.hasSideEffect() && !noteSideEffect()) { + C.Value.getPointer()->printPretty(llvm::errs(), Ctx, C.T); CleanupStack.clear(); return false; } @@ -2009,6 +2008,15 @@ APValue::LValueBase Base = LVal.getLValueBase(); const SubobjectDesignator &Designator = LVal.getLValueDesignator(); + if (auto *VD = LVal.getLValueBase().dyn_cast()) + if (auto *FD = dyn_cast(VD)) + if (FD->isConsteval()) { + Info.FFDiag(Loc, diag::note_consteval_address_accessible) + << !Type->isAnyPointerType(); + Info.FFDiag(FD->getLocation(), diag::note_declared_at); + return false; + } + // Check that the object is a global. Note that the fake 'this' object we // manufacture when checking potential constant expressions is conservatively // assumed to be global here. @@ -2118,6 +2126,11 @@ const auto *FD = dyn_cast_or_null(Member); if (!FD) return true; + if (FD->isConsteval()) { + Info.FFDiag(Loc, diag::note_consteval_address_accessible) << /*pointer*/ 0; + Info.FFDiag(FD->getLocation(), diag::note_declared_at); + return false; + } return Usage == Expr::EvaluateForMangling || FD->isVirtual() || !FD->hasAttr(); } @@ -13592,7 +13605,7 @@ } bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, - const ASTContext &Ctx) const { + const ASTContext &Ctx, bool InPlace) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); @@ -13600,11 +13613,18 @@ EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) + if (InPlace) { + LValue LVal; + if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || + Result.HasSideEffects) + return false; + } else if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) return false; - if (!Info.discardCleanups()) + if (!Info.discardCleanups()) { + this->dumpColor(); llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + } return CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this), Result.Val, Usage) && Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -161,6 +161,7 @@ CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; isConstantEvaluatedOverride = false; + RebuildingImmediateInvocation = false; LoadedExternalKnownNamespaces = false; for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I) Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8823,10 +8823,26 @@ // 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) && !getLangOpts().CPlusPlus2a) { + if (isa(NewFD) && + (!getLangOpts().CPlusPlus2a || ConstexprKind == CSK_consteval)) { Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) << ConstexprKind; + NewFD->setConstexprKind(CSK_unspecified); } + // C++20 [dcl.constexpr]p2: An allocation function, or a + // deallocation function shall not be declared with the consteval + // specifier. + if (ConstexprKind == CSK_consteval) + if (CXXMethodDecl *MD = dyn_cast(NewFD)) + if (MD->getOverloadedOperator() == OO_New || + MD->getOverloadedOperator() == OO_Array_New || + MD->getOverloadedOperator() == OO_Delete || + MD->getOverloadedOperator() == OO_Array_Delete) { + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_invalid_consteval_decl_kind) + << MD; + NewFD->setConstexprKind(CSK_constexpr); + } } // If __module_private__ was specified, mark the function accordingly. @@ -13523,7 +13539,9 @@ // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). if (!isLambdaCallOperator(FD)) - PushExpressionEvaluationContext(ExprEvalContexts.back().Context); + PushExpressionEvaluationContext( + FD->isConsteval() ? ExpressionEvaluationContext::ConstantEvaluated + : ExprEvalContexts.back().Context); // Check for defining attributes before the check for redefinition. if (const auto *Attr = FD->getAttr()) { Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -2299,7 +2299,7 @@ !Expr::isPotentialConstantExpr(Dcl, Diags)) { SemaRef.Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr) - << isa(Dcl); + << isa(Dcl) << Dcl->isConsteval(); for (size_t I = 0, N = Diags.size(); I != N; ++I) SemaRef.Diag(Diags[I].first, Diags[I].second); // Don't return false here: we allow this for compatibility in @@ -7000,7 +7000,9 @@ // If a function is explicitly defaulted on its first declaration, it is // implicitly considered to be constexpr if the implicit declaration // would be. - MD->setConstexprKind(Constexpr ? CSK_constexpr : CSK_unspecified); + MD->setConstexprKind( + Constexpr ? (MD->isConsteval() ? CSK_consteval : CSK_constexpr) + : CSK_unspecified); if (!Type->hasExceptionSpec()) { // C++2a [except.spec]p3: @@ -11427,6 +11429,14 @@ SpecialMem->setType(QT); } +static ConstexprSpecKind +DetermineSpecialMemberConstexprKind(bool IsConstexprValid, + bool HasDefaultedConstevalSM) { + return IsConstexprValid + ? (HasDefaultedConstevalSM ? CSK_consteval : CSK_constexpr) + : CSK_unspecified; +} + CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( CXXRecordDecl *ClassDecl) { // C++ [class.ctor]p5: @@ -11446,9 +11456,12 @@ CXXDefaultConstructor, false); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalDefaultCtor()); + // Create the actual constructor declaration. - CanQualType ClassType - = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); + CanQualType ClassType = + Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); SourceLocation ClassLoc = ClassDecl->getLocation(); DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(ClassType); @@ -11456,8 +11469,7 @@ CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create( Context, ClassDecl, ClassLoc, NameInfo, /*Type*/ QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), - /*isInline=*/true, /*isImplicitlyDeclared=*/true, - Constexpr ? CSK_constexpr : CSK_unspecified); + /*isInline=*/true, /*isImplicitlyDeclared=*/true, ConstexprKind); DefaultCon->setAccess(AS_public); DefaultCon->setDefaulted(); @@ -12343,16 +12355,18 @@ CXXCopyAssignment, Const); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalCopyAssignment()); + // An implicitly-declared copy assignment operator is an inline public // member of its class. 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 ? CSK_constexpr : CSK_unspecified, - SourceLocation()); + CXXMethodDecl *CopyAssignment = + CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), + /*TInfo=*/nullptr, /*StorageClass=*/SC_None, + /*isInline=*/true, ConstexprKind, SourceLocation()); CopyAssignment->setAccess(AS_public); CopyAssignment->setDefaulted(); CopyAssignment->setImplicit(); @@ -12664,16 +12678,18 @@ CXXMoveAssignment, false); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalMoveAssignment()); + // An implicitly-declared move assignment operator is an inline public // member of its class. 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 ? CSK_constexpr : CSK_unspecified, - SourceLocation()); + CXXMethodDecl *MoveAssignment = + CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(), + /*TInfo=*/nullptr, /*StorageClass=*/SC_None, + /*isInline=*/true, ConstexprKind, SourceLocation()); MoveAssignment->setAccess(AS_public); MoveAssignment->setDefaulted(); MoveAssignment->setImplicit(); @@ -13042,6 +13058,9 @@ CXXCopyConstructor, Const); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalCopyCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -13054,8 +13073,7 @@ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), /*isInline=*/true, - /*isImplicitlyDeclared=*/true, - Constexpr ? CSK_constexpr : CSK_unspecified); + /*isImplicitlyDeclared=*/true, ConstexprKind); CopyConstructor->setAccess(AS_public); CopyConstructor->setDefaulted(); @@ -13173,6 +13191,9 @@ CXXMoveConstructor, false); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalMoveCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -13186,8 +13207,7 @@ Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr, ExplicitSpecifier(), /*isInline=*/true, - /*isImplicitlyDeclared=*/true, - Constexpr ? CSK_constexpr : CSK_unspecified); + /*isImplicitlyDeclared=*/true, ConstexprKind); MoveConstructor->setAccess(AS_public); MoveConstructor->setDefaulted(); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -45,6 +45,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace sema; @@ -6027,7 +6028,7 @@ return ExprError(); } - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } ExprResult @@ -15150,6 +15151,157 @@ } } +ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { + if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() || + RebuildingImmediateInvocation) + return E; + + /// Opportunistically remove the callee from ReferencesToConsteval if we can. + /// It's OK if this fails; we'll also remove this in + /// HandleImmediateInvocations, but catching it here allows us to avoid + /// walking the AST looking for it in simple cases. + if (auto *Call = dyn_cast(E.get()->IgnoreImplicit())) + if (auto *DeclRef = + dyn_cast(Call->getCallee()->IgnoreImplicit())) + ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef); + + E = MaybeCreateExprWithCleanups(E); + + ConstantExpr *Res = ConstantExpr::Create( + getASTContext(), E.get(), + ConstantExpr::getStorageKind(E.get()->getType().getTypePtr(), + getASTContext()), + /*IsCausedByImmediateInvocation*/ true); + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; +} + +static void +EvaluateAndDiagnoseImmediateInvocation(Sema &SemaRef, + Sema::IICandidate Candidate) { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + ConstantExpr *CE = Candidate.getPointer(); + if (!CE->EvaluateAsConstantExpr(Eval, Expr::EvaluateForCodeGen, + SemaRef.getASTContext(), true)) { + Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); + FunctionDecl *FD = nullptr; + if (auto *Call = dyn_cast(InnerExpr)) + FD = cast(Call->getCalleeDecl()); + else if (auto *Call = dyn_cast(InnerExpr)) + FD = Call->getMethodDecl(); + else if (auto *Call = dyn_cast(InnerExpr)) + FD = Call->getConstructor(); + else + llvm_unreachable("unhandeld decl kind"); + assert(FD->isConsteval()); + SmallString<256> Buffer; + llvm::raw_svector_ostream OS(Buffer); + FD->printQualifiedName(OS, SemaRef.getASTContext().getPrintingPolicy()); + SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) << Buffer; + for (auto &Note : Notes) + SemaRef.Diag(Note.first, Note.second); + return; + } + CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); +} + +static void RemoveNestedImmediateInvocation( + Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, + llvm::SmallVector::reverse_iterator It) { + struct ComplexRemove : TreeTransform { + using Base = TreeTransform; + llvm::SmallPtrSetImpl &DRSet; + llvm::SmallVector &IISet; + llvm::SmallVector::reverse_iterator CurrentII; + ComplexRemove( + Sema &SemaRef, llvm::SmallPtrSetImpl &DR, + llvm::SmallVector &II, + llvm::SmallVector::reverse_iterator Current) + : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} + ExprResult TransformConstantExpr(ConstantExpr *E) { + if (!E->IsCausedByImmediateInvocation()) + return Base::TransformConstantExpr(E); + auto It = + std::find_if(CurrentII, IISet.rend(), [E](Sema::IICandidate Elem) { + return Elem.getPointer() == E; + }); + assert(It != IISet.rend() && + "ConstantExpr marked IsCausedByImmediateInvocation should " + "be present"); + It->setInt(1); // Mark as deleted + return Base::TransformExpr(E->getSubExpr()); + } + /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so + /// we need to remove its DeclRefExpr from the DRSet. + ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { + DRSet.erase(cast(E->getCallee()->IgnoreImplicit())); + return Base::TransformCXXOperatorCallExpr(E); + } + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return E; + } + bool AlwaysRebuild() { return false; } + bool ReplacingOriginal() { return true; } + } Transfromer(SemaRef, Rec.ReferenceToConsteval, + Rec.ImmediateInvocationCandidates, It); + ExprResult Res = Transfromer.TransformExpr(It->getPointer()->getSubExpr()); + assert(Res.isUsable()); + Res = SemaRef.MaybeCreateExprWithCleanups(Res); + It->getPointer()->setSubExpr(Res.get()); +} + +static void +HandleImmediateInvocations(Sema &SemaRef, + Sema::ExpressionEvaluationContextRecord &Rec) { + if ((Rec.ImmediateInvocationCandidates.size() == 0 && + Rec.ReferenceToConsteval.size() == 0) || + SemaRef.RebuildingImmediateInvocation) + return; + + /// When we have more then 1 ImmediateInvocationCandidates we need to check + /// for nested ImmediateInvocationCandidates. when we have only 1 we only + /// need to remove ReferenceToConsteval in the immediate invocation. + if (Rec.ImmediateInvocationCandidates.size() > 1) { + + /// Prevent sema calls during the tree transform from adding pointers that + /// are already in the sets. + llvm::SaveAndRestore DisableIITracking( + SemaRef.RebuildingImmediateInvocation, true); + + /// Prevent diagnostic during tree transfrom as they are duplicates + Sema::TentativeAnalysisScope DisableDiag(SemaRef); + + for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); + It != Rec.ImmediateInvocationCandidates.rend(); It++) + if (!It->getInt()) + RemoveNestedImmediateInvocation(SemaRef, Rec, It); + } else if (Rec.ImmediateInvocationCandidates.size() == 1 && + Rec.ReferenceToConsteval.size()) { + struct SimpleRemove : RecursiveASTVisitor { + llvm::SmallPtrSetImpl &DRSet; + SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} + bool VisitDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return DRSet.size(); + } + } Visitor(Rec.ReferenceToConsteval); + Visitor.TraverseStmt( + Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); + } + for (auto CE : Rec.ImmediateInvocationCandidates) + if (!CE.getInt()) + EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + for (auto DR : Rec.ReferenceToConsteval) { + auto *FD = cast(DR->getDecl()); + SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) + << FD; + SemaRef.Diag(FD->getLocation(), diag::note_declared_at); + } +} + void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; @@ -15183,6 +15335,7 @@ } WarnOnPendingNoDerefs(Rec); + HandleImmediateInvocations(*this, Rec); // Warn on any volatile-qualified simple-assignments that are not discarded- // value expressions nor unevaluated operands (those cases get removed from @@ -16905,6 +17058,11 @@ if (Method->isVirtual() && !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) OdrUse = false; + + if (auto *FD = dyn_cast(E->getDecl())) + if (!isConstantEvaluated() && FD->isConsteval() && + !RebuildingImmediateInvocation) + ExprEvalContexts.back().ReferenceToConsteval.insert(E); MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse); } Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1227,7 +1227,9 @@ // Enter a new evaluation context to insulate the lambda from any // cleanups from the enclosing full-expression. PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated); + LSI->CallOperator->isConsteval() + ? ExpressionEvaluationContext::ConstantEvaluated + : ExpressionEvaluationContext::PotentiallyEvaluated); } void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope, Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -12654,8 +12654,7 @@ if (CheckFunctionCall(FnDecl, TheCall, FnDecl->getType()->castAs())) return ExprError(); - - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FnDecl); } else { // We matched a built-in operator. Convert the arguments, then // break out so that we will build the appropriate built-in @@ -13024,7 +13023,7 @@ if (Best->RewriteKind != CRK_None) R = new (Context) CXXRewrittenBinaryOperator(R.get(), IsReversed); - return R; + return CheckForImmediateInvocation(R, FnDecl); } else { // We matched a built-in operator. Convert the arguments, then // break out so that we will build the appropriate built-in @@ -13580,7 +13579,8 @@ MemExpr->getMemberLoc()); } - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), + TheCall->getMethodDecl()); } /// BuildCallToObjectOfClassType - Build a call to an object of class @@ -13863,7 +13863,7 @@ if (CheckFunctionCall(Method, TheCall, Proto)) return true; - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method); } /// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator-> @@ -14059,7 +14059,7 @@ if (CheckFunctionCall(FD, UDL, nullptr)) return ExprError(); - return MaybeBindToTemporary(UDL); + return CheckForImmediateInvocation(MaybeBindToTemporary(UDL), FD); } /// Build a call to 'begin' or 'end' for a C++11 for-range statement. If the Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -3448,9 +3448,6 @@ if (!Init) return Init; - if (auto *FE = dyn_cast(Init)) - Init = FE->getSubExpr(); - if (auto *AIL = dyn_cast(Init)) Init = AIL->getCommonExpr(); Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -Wno-unused-value %s -verify namespace basic_sema { @@ -12,6 +12,7 @@ } constexpr auto l_eval = [](int i) consteval { +// expected-note@-1+ {{declared here}} return i; }; @@ -23,11 +24,12 @@ struct A { consteval int f1(int i) const { +// expected-note@-1 {{declared here}} return i; } consteval A(int i); consteval A() = default; - consteval ~A() = default; + consteval ~A() = default; // expected-error {{destructor cannot be declared consteval}} }; consteval struct B {}; // expected-error {{struct cannot be marked consteval}} @@ -51,14 +53,296 @@ struct D { C c; consteval D() = default; // expected-error {{cannot be consteval}} - consteval ~D() = default; // expected-error {{cannot be consteval}} + consteval ~D() = default; // expected-error {{destructor cannot be declared consteval}} }; -struct E : C { // expected-note {{here}} - consteval ~E() {} // expected-error {{cannot be declared consteval because base class 'basic_sema::C' does not have a constexpr destructor}} +struct E : C { + consteval ~E() {} // expected-error {{cannot be declared consteval}} }; } consteval int main() { // expected-error {{'main' is not allowed to be declared consteval}} return 0; } + +consteval int f_eval(int i) { +// expected-note@-1+ {{declared here}} + return i; +} + +namespace taking_address { + +using func_type = int(int); + +func_type* p1 = (&f_eval); +// expected-error@-1 {{take address}} +func_type* p7 = __builtin_addressof(f_eval); +// expected-error@-1 {{take address}} + +auto p = f_eval; +// expected-error@-1 {{take address}} + +auto m1 = &basic_sema::A::f1; +// expected-error@-1 {{take address}} +auto l1 = &decltype(basic_sema::l_eval)::operator(); +// expected-error@-1 {{take address}} + +consteval int f(int i) { +// expected-note@-1+ {{declared here}} + return i; +} + +auto ptr = &f; +// expected-error@-1 {{take address}} + +auto f1() { + return &f; +// expected-error@-1 {{take address}} +} + +} + +namespace invalid_function { +using size_t = unsigned long; +struct A { + consteval void *operator new(size_t count); + // expected-error@-1 {{'operator new' cannot be declared consteval}} + consteval void *operator new[](size_t count); + // expected-error@-1 {{'operator new[]' cannot be declared consteval}} + consteval void operator delete(void* ptr); + // expected-error@-1 {{'operator delete' cannot be declared consteval}} + consteval void operator delete[](void* ptr); + // expected-error@-1 {{'operator delete[]' cannot be declared consteval}} + consteval ~A() {} + // expected-error@-1 {{destructor cannot be declared consteval}} +}; + +} + +namespace nested { +consteval int f() { + return 0; +} + +consteval int f1(...) { + return 1; +} + +enum E {}; + +using T = int(&)(); + +consteval auto operator+ (E, int(*a)()) { + return 0; +} + +void d() { + auto i = f1(E() + &f); +} + +auto l0 = [](auto) consteval { + return 0; +}; + +int i0 = l0(&f1); + +int i1 = f1(l0(4)); + +int i2 = f1(&f1, &f1, &f1, &f1, &f1, &f1, &f1); + +int i3 = f1(f1(f1(&f1, &f1), f1(&f1, &f1), f1(f1(&f1, &f1), &f1))); + +} + +namespace user_defined_literal { + +consteval int operator"" _test(unsigned long long i) { +// expected-note@-1+ {{declared here}} + return 0; +} + +int i = 0_test; + +auto ptr = &operator"" _test; +// expected-error@-1 {{take address}} + +} + +namespace return_address { + +consteval int f() { + return 0; +} + +consteval int(*ret1(int i))() { + return &f; +} + +auto ptr = ret1(0); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{pointer to a consteval}} + +struct A { + consteval int f(int) { + return 0; + } +}; + +using mem_ptr_type = int (A::*)(int); + +template +struct C {}; + +C<&A::f> c; +// expected-error@-1 {{is not a constant expression}} +// expected-note@-2 {{pointer to a consteval}} + +consteval mem_ptr_type ret2() { + return &A::f; +} + +C c1; +// expected-error@-1 {{is not a constant expression}} +// expected-note@-2 {{pointer to a consteval}} + +} + +namespace context { + +int g_i; +// expected-note@-1 {{declared here}} + +consteval int f(int) { + return 0; +} + +constexpr int c_i = 0; + +int t1 = f(g_i); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{read of non-const variable}} +int t3 = f(c_i); + +constexpr int f_c(int i) { +// expected-note@-1 {{declared here}} + int t = f(i); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{read of non-const variable}} + return f(0); +} + +consteval int f_eval(int i) { + return f(i); +} + +auto l0 = [](int i) consteval { + return f(i); +}; + +auto l1 = [](int i) constexpr { +// expected-note@-1 {{declared here}} + int t = f(i); +// expected-error@-1 {{could not be evaluated}} +// expected-note@-2 {{read of non-const variable}} + return f(0); +}; + +} + +namespace temporaries { + +struct A { + consteval int ret_i() const { return 0; } + consteval A ret_a() const { return A{}; } + constexpr ~A() { } +}; + +consteval int by_value_a(A a) { return a.ret_i(); } + +consteval int const_a_ref(const A &a) { + return a.ret_i(); +} + +consteval int rvalue_ref(const A &&a) { + return a.ret_i(); +} + +consteval const A &to_lvalue_ref(const A &&a) { + return a; +} + +void test() { + constexpr A a {}; + { int k = A().ret_i(); } + { A k = A().ret_a(); } + { A k = to_lvalue_ref(A()); }// expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} + { A k = to_lvalue_ref(A().ret_a()); } // expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} + { int k = A().ret_a().ret_i(); } + { int k = by_value_a(A()); } + { int k = const_a_ref(A()); } + { int k = const_a_ref(a); } + { int k = rvalue_ref(A()); } + { int k = rvalue_ref(static_cast(a)); } + { int k = const_a_ref(A().ret_a()); } + { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } + { int k = const_a_ref(to_lvalue_ref(static_cast(a))); } + { int k = by_value_a(A().ret_a()); } + { int k = by_value_a(to_lvalue_ref(static_cast(a))); } + { int k = (A().ret_a(), A().ret_i()); } + { int k = (const_a_ref(A().ret_a()), A().ret_i()); }// +} + +} + +namespace alloc { + +struct A { + int* p = new int(42); + // expected-note@-1+ {{heap allocation performed here}} + consteval int ret_i() const { return p ? *p : 0; } + consteval A ret_a() const { return A{}; } + constexpr ~A() { delete p; } +}; + +consteval int by_value_a(A a) { return a.ret_i(); } + +consteval int const_a_ref(const A &a) { + return a.ret_i(); +} + +consteval int rvalue_ref(const A &&a) { + return a.ret_i(); +} + +consteval const A &to_lvalue_ref(const A &&a) { + return a; +} + +void test() { + constexpr A a{ nullptr }; + { int k = A().ret_i(); } + { A k = A().ret_a(); } // expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} + { A k = to_lvalue_ref(A()); } // expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} + { A k = to_lvalue_ref(A().ret_a()); } // expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} + { int k = A().ret_a().ret_i(); } + { int k = by_value_a(A()); } + { int k = const_a_ref(A()); } + { int k = const_a_ref(a); } + { int k = rvalue_ref(A()); } + { int k = rvalue_ref(static_cast(a)); } + { int k = const_a_ref(A().ret_a()); } + { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } + { int k = const_a_ref(to_lvalue_ref(static_cast(a))); } + { int k = by_value_a(A().ret_a()); } + { int k = by_value_a(to_lvalue_ref(static_cast(a))); } + { int k = (A().ret_a(), A().ret_i()); }// expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} + { int k = (const_a_ref(A().ret_a()), A().ret_i()); } +} + +} \ No newline at end of file