Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -693,7 +693,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. @@ -989,7 +990,7 @@ "invalid accessor"); return *getTrailingObjects(); } - const APValue &APValueResult() const { + APValue &APValueResult() const { return const_cast(this)->APValueResult(); } @@ -1003,7 +1004,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 IsImmediateInvocation = false); static ConstantExpr *CreateEmpty(const ASTContext &Context, ResultStorageKind StorageKind, EmptyShell Empty); @@ -1034,8 +1036,11 @@ ResultStorageKind getResultStorageKind() const { return static_cast(ConstantExprBits.ResultKind); } + bool isImmediateInvocation() const { + return ConstantExprBits.IsImmediateInvocation; + } APValue getAPValueResult() const; - const APValue &getResultAsAPValue() const { return APValueResult(); } + APValue &getResultAsAPValue() const { return APValueResult(); } llvm::APSInt getResultAsAPSInt() const; // Iterators child_range children() { return child_range(&SubExpr, &SubExpr+1); } 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 IsImmediateInvocation : 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 @@ -2387,6 +2387,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">; @@ -2484,7 +2491,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 @@ -811,6 +811,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 = false; + /// Used to change context to isConstantEvaluated without pushing a heavy /// ExpressionEvaluationContextRecord object. bool isConstantEvaluatedOverride; @@ -1022,6 +1027,8 @@ PotentiallyEvaluatedIfUsed }; + using ImmediateInvocationCandidate = llvm::PointerIntPair; + /// Data structure used to record current or nested /// expression evaluation contexts. struct ExpressionEvaluationContextRecord { @@ -1068,6 +1075,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 { @@ -5430,6 +5444,10 @@ /// it simply returns the passed in expression. ExprResult MaybeBindToTemporary(Expr *E); + /// Wrap the expression in a ConstantExpr if it is a potential immediate + /// invocation. + ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl); + bool CompleteConstructorCall(CXXConstructorDecl *Constructor, MultiExprArg ArgsPtr, SourceLocation Loc, Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -280,7 +280,8 @@ } ConstantExpr *ConstantExpr::Create(const ASTContext &Context, Expr *E, - ResultStorageKind StorageKind) { + ResultStorageKind StorageKind, + bool IsImmediateInvocation) { assert(!isa(E)); AssertResultStorageKind(StorageKind); unsigned Size = totalSizeToAlloc( @@ -288,6 +289,8 @@ StorageKind == ConstantExpr::RSK_Int64); void *Mem = Context.Allocate(Size, alignof(ConstantExpr)); ConstantExpr *Self = new (Mem) ConstantExpr(E, StorageKind); + Self->ConstantExprBits.IsImmediateInvocation = + IsImmediateInvocation; return Self; } @@ -317,7 +320,7 @@ } void ConstantExpr::MoveIntoResult(APValue &Value, const ASTContext &Context) { - assert(getStorageKind(Value) == ConstantExprBits.ResultKind && + assert(getStorageKind(Value) <= ConstantExprBits.ResultKind && "Invalid storage for this value kind"); ConstantExprBits.APValueKind = Value.getKind(); switch (ConstantExprBits.ResultKind) { Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -2004,6 +2004,17 @@ 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.Note(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. @@ -2113,6 +2124,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.Note(FD->getLocation(), diag::note_declared_at); + return false; + } return Usage == Expr::EvaluateForMangling || FD->isVirtual() || !FD->hasAttr(); } @@ -3547,7 +3563,13 @@ APValue *BaseVal = nullptr; QualType BaseType = getType(LVal.Base); - if (const ValueDecl *D = LVal.Base.dyn_cast()) { + if (const ConstantExpr *CE = + dyn_cast_or_null(LVal.Base.dyn_cast())) { + /// Nested immediate invocation have been previously removed so if we found + /// a ConstantExpr it can only be the EvaluatingDecl. + assert(CE->isImmediateInvocation() && CE == Info.EvaluatingDecl); + BaseVal = Info.EvaluatingDeclValue; + } else if (const ValueDecl *D = LVal.Base.dyn_cast()) { // In C++98, const, non-volatile integers initialized with ICEs are ICEs. // In C++11, constexpr, non-volatile variables initialized with constant // expressions are constant expressions too. Inside constexpr functions, @@ -13580,7 +13602,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."); @@ -13588,7 +13610,14 @@ EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) + if (InPlace) { + Info.setEvaluatingDecl(this, Result.Val); + LValue LVal; + LVal.set(this); + 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()) Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8876,9 +8876,24 @@ // 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 && + (NewFD->getOverloadedOperator() == OO_New || + NewFD->getOverloadedOperator() == OO_Array_New || + NewFD->getOverloadedOperator() == OO_Delete || + NewFD->getOverloadedOperator() == OO_Array_Delete)) { + Diag(D.getDeclSpec().getConstexprSpecLoc(), + diag::err_invalid_consteval_decl_kind) + << NewFD; + NewFD->setConstexprKind(CSK_constexpr); } } @@ -13585,7 +13600,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: Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -46,6 +46,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; @@ -6048,7 +6049,7 @@ return ExprError(); } - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } ExprResult @@ -15176,6 +15177,170 @@ } } +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()), + /*IsImmediateInvocation*/ true); + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; +} + +static void EvaluateAndDiagnoseImmediateInvocation( + Sema &SemaRef, Sema::ImmediateInvocationCandidate 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->getConstructor(); + else + llvm_unreachable("unhandled 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, + SmallVector::reverse_iterator It) { + struct ComplexRemove : TreeTransform { + using Base = TreeTransform; + llvm::SmallPtrSetImpl &DRSet; + SmallVector &IISet; + SmallVector::reverse_iterator + CurrentII; + ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, + SmallVector &II, + SmallVector::reverse_iterator Current) + : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} + void RemoveImmediateInvocation(ConstantExpr* E) { + auto It = std::find_if(CurrentII, IISet.rend(), + [E](Sema::ImmediateInvocationCandidate Elem) { + return Elem.getPointer() == E; + }); + assert(It != IISet.rend() && + "ConstantExpr marked IsImmediateInvocation should " + "be present"); + It->setInt(1); // Mark as deleted + } + ExprResult TransformConstantExpr(ConstantExpr *E) { + if (!E->isImmediateInvocation()) + return Base::TransformConstantExpr(E); + RemoveImmediateInvocation(E); + 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); + } + /// Base::TransformInitializer skip ConstantExpr so we need to visit them + /// here. + ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) { + if (!Init) + return Init; + /// ConstantExpr are the first layer of implicit node to be removed so if + /// Init isn't a ConstantExpr, no ConstantExpr will be skipped. + if (auto *CE = dyn_cast(Init)) + if (CE->isImmediateInvocation()) + RemoveImmediateInvocation(CE); + return Base::TransformInitializer(Init, NotCopyInit); + } + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return E; + } + bool AlwaysRebuild() { return false; } + bool ReplacingOriginal() { return true; } + } Transformer(SemaRef, Rec.ReferenceToConsteval, + Rec.ImmediateInvocationCandidates, It); + ExprResult Res = Transformer.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; @@ -15209,6 +15374,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 @@ -16931,6 +17097,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 @@ -1231,7 +1231,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/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,329 @@ 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}} + +consteval auto operator"" _test1(unsigned long long i) { + return &f_eval; +} + +auto i1 = 0_test1; // expected-error {{could not be evaluated}} +// expected-note@-1 {{is not a constant expression}} + +} + +namespace return_address { + +consteval int f() { +// expected-note@-1 {{declared here}} + 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) { + // expected-note@-1+ {{declared here}} + 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()); } +} + +} + +namespace self_referencing { + +struct S { + S* ptr = nullptr; + constexpr S(int i) : ptr(this) { + if (this == ptr && i) + ptr = nullptr; + } + constexpr ~S() {} +}; + +consteval S f(int i) { + return S(i); +} + +void test() { + S s(1); + s = f(1); + s = f(0); // expected-error {{could not be evaluated}} + // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} +} + +}