Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -461,6 +461,9 @@ /// either by the user or implicitly. unsigned DeclaredSpecialMembers : 6; + /// The special member which have been defaulted with the consteval keyword. + unsigned DefaultedSpecialMemberIsConsteval : 6; + /// Whether an implicit copy constructor could have a const-qualified /// parameter, for initializing virtual bases and for other subobjects. unsigned ImplicitCopyConstructorCanHaveConstParamForVBase : 1; @@ -1012,6 +1015,42 @@ 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 default + /// 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/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -53,6 +53,9 @@ def note_constexpr_non_global : Note< "%select{pointer|reference}0 to %select{|subobject of }1" "%select{temporary|%3}2 is not a constant expression">; +def note_consteval_address_accessible : Note< + "%select{pointer|reference}0 on 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 @@ -2313,6 +2313,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< + "operator %select{new|delete|new[]|delete[]}0 cannot be declared consteval">; def err_invalid_constexpr : Error< "%select{function parameter|typedef|non-static data member}0 " "cannot be %select{constexpr|consteval}1">; @@ -2398,7 +2405,7 @@ "variables defined in a constexpr %select{function|constructor}0 must be " "initialized">; def ext_constexpr_function_never_constant_expr : ExtWarn< - "constexpr %select{function|constructor}0 never produces a " + "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a " "constant expression">, InGroup>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; @@ -2436,7 +2443,7 @@ def err_constexpr_union_ctor_no_init : Error< "constexpr union constructor does not initialize any member">; def err_constexpr_ctor_missing_init : Error< - "constexpr constructor must initialize all members">; + "%select{constexpr|consteval}0 constructor must initialize all members">; def note_constexpr_ctor_missing_init : Note< "member not initialized by constructor">; def note_non_literal_no_constexpr_ctors : Note< Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -803,6 +803,11 @@ } }; + /// Should be set for most operation. + /// It is unset to disable tracking of immediate invocation candidates and + /// reference on consteval. + bool hasImmediateInvocationTracking; + /// Used to change context to isConstantEvaluated without pushing a heavy /// ExpressionEvaluationContextRecord object. bool isConstantEvaluatedOverride; @@ -1062,6 +1067,14 @@ llvm::SmallPtrSet PossibleDerefs; + /// Set of candidate for starting an immediate invocation. + llvm::SmallVector, 8> + ImmediateInvocationsCandidates; + + /// Set of DeclRefExpr referencing a consteval function when used in a + /// context not already know to be immediately invoked. + llvm::SmallPtrSet ReferenceOnConsteval; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -5225,6 +5238,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 @@ -97,6 +97,7 @@ HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), + DefaultedSpecialMemberIsConsteval(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), @@ -738,8 +739,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) { @@ -749,8 +753,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 @@ -781,6 +790,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 @@ -800,15 +811,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/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -1926,6 +1926,14 @@ 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(); + 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. @@ -2007,6 +2015,10 @@ 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; + return false; + } return Usage == Expr::EvaluateForMangling || FD->isVirtual() || !FD->hasAttr(); } Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -160,6 +160,7 @@ CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; isConstantEvaluatedOverride = false; + hasImmediateInvocationTracking = true; 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 @@ -8662,6 +8662,22 @@ if (isa(NewFD)) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) << (ConstexprKind == CSK_consteval); + // C++20 [dcl.constexpr]p2: A destructor, an allocation function, or a + // deallocation function shall not be declared with the consteval + // specifier. + if (ConstexprKind == CSK_consteval) { + if (CXXMethodDecl *MD = dyn_cast(NewFD)) { + if (MD->getOverloadedOperator() == OO_New || + MD->getOverloadedOperator() == OO_Array_New || + MD->getOverloadedOperator() == OO_Delete || + MD->getOverloadedOperator() == OO_Array_Delete) { + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_invalid_consteval_decl_kind) + << MD->getOverloadedOperator() - OO_New; + NewFD->setConstexprKind(CSK_constexpr); + } + } + } } // If __module_private__ was specified, mark the function accordingly. @@ -13025,7 +13041,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 @@ -1872,7 +1872,8 @@ if (!Inits.count(Field)) { if (Kind == Sema::CheckConstexprKind::Diagnose) { if (!Diagnosed) { - SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init); + SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init) + << Dcl->isConsteval(); Diagnosed = true; } SemaRef.Diag(Field->getLocation(), @@ -2230,7 +2231,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 @@ -6823,7 +6824,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: @@ -11139,6 +11142,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: @@ -11158,9 +11169,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); @@ -11168,8 +11182,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(); @@ -12049,16 +12062,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(); @@ -12370,16 +12385,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(); @@ -12748,6 +12765,9 @@ CXXCopyConstructor, Const); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalCopyCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -12760,8 +12780,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(); @@ -12879,6 +12898,9 @@ CXXMoveConstructor, false); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalMoveCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -12892,8 +12914,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; @@ -6001,7 +6002,7 @@ return ExprError(); } - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } ExprResult @@ -14920,6 +14921,125 @@ Rec.PossibleDerefs.clear(); } +ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { + if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() || + !hasImmediateInvocationTracking) + return E; + ConstantExpr *Res = ConstantExpr::Create( + getASTContext(), E.get(), + ConstantExpr::getStorageKind(E.get()->getType().getTypePtr(), + getASTContext())); + ExprEvalContexts.back().ImmediateInvocationsCandidates.emplace_back(Res, 0); + return Res; +} + +static void EvaluateAndDiagnoseImmediateInvocation(Sema &SemaRef, + ConstantExpr *CE) { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + if (!CE->getSubExpr()->EvaluateAsConstantExpr(Eval, Expr::EvaluateForCodeGen, + SemaRef.getASTContext())) { + SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) + << cast( + cast(CE->getSubExpr())->getCalleeDecl()); + for (auto &Note : Notes) + SemaRef.Diag(Note.first, Note.second); + return; + } + CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); +} + +static void +HandleImediateInvocations(Sema &SemaRef, + Sema::ExpressionEvaluationContextRecord &Rec) { + if ((Rec.ImmediateInvocationsCandidates.size() == 0 && + Rec.ReferenceOnConsteval.size() == 0) || + !SemaRef.hasImmediateInvocationTracking) + return; + struct ExprCompType { + bool operator()(llvm::PointerIntPair LHS, + llvm::PointerIntPair RHS) { + return LHS.getPointer()->getBeginLoc() < RHS.getPointer()->getBeginLoc(); + } + }; + + llvm::sort(Rec.ImmediateInvocationsCandidates, ExprCompType{}); + + /// When we have more then 1 ImmediateInvocationsCandidates we need to check + /// for nested ImmediateInvocationsCandidates. when we have only 1 we only + /// need to remove ReferenceOnConsteval in the imediate invocation. + if (Rec.ImmediateInvocationsCandidates.size() > 1) { + + /// Prevent sema calls during the tree transform from adding pointers that + /// are already in the sets. + llvm::SaveAndRestore DisableIITracking( + SemaRef.hasImmediateInvocationTracking, false); + + for (auto &CE : Rec.ImmediateInvocationsCandidates) + if (!CE.getInt()) { + /// Prevent diagnostic during tree transfrom as they are duplicates + Sema::TentativeAnalysisScope DisableDiag(SemaRef); + struct ComplexRemove : TreeTransform { + using Base = TreeTransform; + llvm::SmallPtrSetImpl &DRSet; + decltype(Rec.ImmediateInvocationsCandidates) &IISet; + ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, + decltype(Rec.ImmediateInvocationsCandidates) &II) + : Base(SemaRef), DRSet(DR), IISet(II) {} + ExprResult TransformConstantExpr(ConstantExpr *E) { + auto It = llvm::lower_bound( + IISet, llvm::PointerIntPair(E, 0), + ExprCompType{}); + if (It != IISet.end()) { + It->setInt(1); // Mark as deleted + return Base::TransformExpr(It->getPointer()->getSubExpr()); + } else + return Base::TransformConstantExpr(It->getPointer()); + } + /// 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; + } + /// FIXME: Add an option to tree transfrom that prevents rebuilding + /// nodes that only need rewiring and use this option here. + bool AlwaysRebuild() { return false; } + bool ReplacingOriginal() { return true; } + } Transfromer(SemaRef, Rec.ReferenceOnConsteval, + Rec.ImmediateInvocationsCandidates); + ExprResult Res = + Transfromer.TransformExpr(CE.getPointer()->getSubExpr()); + CE.getPointer()->setSubExpr(Res.get()); + } + } else if (Rec.ImmediateInvocationsCandidates.size() == 1) { + struct SimpleRemove : RecursiveASTVisitor { + llvm::SmallPtrSetImpl &DRSet; + SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} + bool VisitDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return DRSet.size(); + } + } Visitor(Rec.ReferenceOnConsteval); + Visitor.TraverseStmt( + Rec.ImmediateInvocationsCandidates.front().getPointer()->getSubExpr()); + } + for (auto CE : Rec.ImmediateInvocationsCandidates) + if (!CE.getInt()) + EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE.getPointer()); + for (auto DR : Rec.ReferenceOnConsteval) { + 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; @@ -14953,6 +15073,7 @@ } WarnOnPendingNoDerefs(Rec); + HandleImediateInvocations(*this, Rec); // When are coming out of an unevaluated context, clear out any // temporaries that we may have created as part of the evaluation of @@ -16656,6 +16777,11 @@ if (Method->isVirtual() && !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) OdrUse = false; + + if (auto *FD = dyn_cast(E->getDecl())) + if (!isConstantEvaluated() && FD->isConsteval() && + hasImmediateInvocationTracking) + ExprEvalContexts.back().ReferenceOnConsteval.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 @@ -1194,7 +1194,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 @@ -12418,8 +12418,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 @@ -12662,7 +12661,8 @@ isa(FnDecl), OpLoc, TheCall->getSourceRange(), VariadicDoesNotApply); - 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 @@ -13499,7 +13499,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-> @@ -13695,7 +13695,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 @@ -11104,7 +11104,7 @@ RecoveryTSI); } -template +template ExprResult TreeTransform::TransformCXXConstructExpr(CXXConstructExpr *E) { // CXXConstructExprs other than for list-initialization and Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -1696,6 +1696,7 @@ Data.ComputedVisibleConversions = Record.readInt(); Data.UserProvidedDefaultConstructor = Record.readInt(); Data.DeclaredSpecialMembers = Record.readInt(); + Data.DefaultedSpecialMemberIsConsteval = Record.readInt(); Data.ImplicitCopyConstructorCanHaveConstParamForVBase = Record.readInt(); Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase = Record.readInt(); Data.ImplicitCopyAssignmentHasConstParam = Record.readInt(); Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -6176,6 +6176,7 @@ Record->push_back(Data.ComputedVisibleConversions); Record->push_back(Data.UserProvidedDefaultConstructor); Record->push_back(Data.DeclaredSpecialMembers); + Record->push_back(Data.DefaultedSpecialMemberIsConsteval); Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForVBase); Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase); Record->push_back(Data.ImplicitCopyAssignmentHasConstParam); Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -12,6 +12,7 @@ } constexpr auto l_eval = [](int i) consteval { +// expected-note@-1+ {{declared here}} return i; }; @@ -23,6 +24,7 @@ struct A { consteval int f1(int i) const { +// expected-note@-1 {{declared here}} return i; } consteval A(int i); @@ -56,3 +58,183 @@ 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 marked 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)); + +} + +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 on 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 on a consteval}} + +consteval mem_ptr_type ret2() { + return &A::f; +} + +C c1; +// expected-error@-1 {{is not a constant expression}} +// expected-note@-2 {{pointer on 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); +}; + +} \ No newline at end of file