Tyker updated this revision to Diff 223021. Tyker marked 16 inline comments as done. Tyker added a comment.
i addressed almost all comments. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D63960/new/ https://reviews.llvm.org/D63960 Files: clang/include/clang/AST/DeclCXX.h clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/DeclCXX.cpp clang/lib/AST/ExprConstant.cpp clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaLambda.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriter.cpp clang/test/SemaCXX/cxx2a-consteval.cpp
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); @@ -62,3 +64,203 @@ 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}} +}; + +} + +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<mem_ptr_type ptr> +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<ret2()> 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 cleanup { + +struct A { + int *p = new int(42); + consteval int get() { return *p; } + constexpr ~A() { delete p; } +}; +int k1 = A().get(); + +struct B { + int *p = new int(42); + consteval int get() { return *p; } + consteval ~B() { delete p; } +}; +int k2 = B().get(); + +} \ No newline at end of file Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -6183,6 +6183,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/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -1700,6 +1700,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/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -12436,8 +12436,7 @@ if (CheckFunctionCall(FnDecl, TheCall, FnDecl->getType()->castAs<FunctionProtoType>())) 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 @@ -12680,7 +12679,8 @@ isa<CXXMethodDecl>(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 @@ -13517,7 +13517,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-> @@ -13713,7 +13713,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/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1197,7 +1197,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/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; @@ -5994,7 +5995,7 @@ return ExprError(); } - return MaybeBindToTemporary(TheCall); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } ExprResult @@ -15059,6 +15060,138 @@ Rec.PossibleDerefs.clear(); } +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<CallExpr>(E.get()->IgnoreImplicit())) + if (auto *DeclRef = + dyn_cast<DeclRefExpr>(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())); + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; +} + +static void EvaluateAndDiagnoseImmediateInvocation(Sema &SemaRef, + ConstantExpr *CE) { + llvm::SmallVector<PartialDiagnosticAt, 8> 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<FunctionDecl>( + cast<CallExpr>(CE->getSubExpr())->getCalleeDecl()); + for (auto &Note : Notes) + SemaRef.Diag(Note.first, Note.second); + return; + } + CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); +} + +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<bool> 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()) { + struct ComplexRemove : TreeTransform<ComplexRemove> { + using Base = TreeTransform<ComplexRemove>; + llvm::SmallPtrSetImpl<DeclRefExpr *> &DRSet; + decltype(Rec.ImmediateInvocationCandidates) &IISet; + decltype( + Rec.ImmediateInvocationCandidates)::reverse_iterator CurrentII; + ComplexRemove( + Sema &SemaRef, llvm::SmallPtrSetImpl<DeclRefExpr *> &DR, + decltype(Rec.ImmediateInvocationCandidates) &II, + decltype( + Rec.ImmediateInvocationCandidates)::reverse_iterator Current) + : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} + ExprResult TransformConstantExpr(ConstantExpr *E) { + E->dumpColor(); + auto It = + std::find_if(CurrentII, IISet.rend(), + [E](llvm::PointerIntPair<ConstantExpr *, 1> Elem) { + return Elem.getPointer() == E; + }); + if (It != IISet.rend()) { + 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<DeclRefExpr>(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()); + It->getPointer()->setSubExpr(Res.get()); + } + } else if (Rec.ImmediateInvocationCandidates.size() == 1 && + Rec.ReferenceToConsteval.size()) { + struct SimpleRemove : RecursiveASTVisitor<SimpleRemove> { + llvm::SmallPtrSetImpl<DeclRefExpr *> &DRSet; + SimpleRemove(llvm::SmallPtrSetImpl<DeclRefExpr *> &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.getPointer()); + for (auto DR : Rec.ReferenceToConsteval) { + auto *FD = cast<FunctionDecl>(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; @@ -15092,6 +15225,7 @@ } WarnOnPendingNoDerefs(Rec); + HandleImmediateInvocations(*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 @@ -16789,6 +16923,11 @@ if (Method->isVirtual() && !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) OdrUse = false; + + if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl())) + if (!isConstantEvaluated() && FD->isConsteval() && + !RebuildingImmediateInvocation) + ExprEvalContexts.back().ReferenceToConsteval.insert(E); MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse); } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -2292,7 +2292,7 @@ !Expr::isPotentialConstantExpr(Dcl, Diags)) { SemaRef.Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr) - << isa<CXXConstructorDecl>(Dcl); + << isa<CXXConstructorDecl>(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 @@ -6910,7 +6910,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: @@ -11225,6 +11227,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: @@ -11244,9 +11254,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); @@ -11254,8 +11267,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(); @@ -12140,16 +12152,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(); @@ -12461,16 +12475,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(); @@ -12839,6 +12855,9 @@ CXXCopyConstructor, Const); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalCopyCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -12851,8 +12870,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(); @@ -12970,6 +12988,9 @@ CXXMoveConstructor, false); + ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind( + Constexpr, ClassDecl->hasDefaultedConstevalMoveCtor()); + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -12983,8 +13004,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/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8788,6 +8788,20 @@ Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor) << ConstexprKind; } + // 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<CXXMethodDecl>(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. @@ -13455,7 +13469,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<AliasAttr>()) { 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; + RebuildingImmediateInvocation = false; LoadedExternalKnownNamespaces = false; for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I) Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -1941,6 +1941,15 @@ APValue::LValueBase Base = LVal.getLValueBase(); const SubobjectDesignator &Designator = LVal.getLValueDesignator(); + if (auto *VD = LVal.getLValueBase().dyn_cast<const ValueDecl *>()) + if (auto *FD = dyn_cast<FunctionDecl>(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. @@ -2050,6 +2059,11 @@ const auto *FD = dyn_cast_or_null<CXXMethodDecl>(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<DLLImportAttr>(); } Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -98,6 +98,7 @@ DefaultedDestructorIsConstexpr(true), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), + DefaultedSpecialMemberIsConsteval(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), @@ -754,8 +755,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) { @@ -765,8 +769,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 @@ -797,6 +806,8 @@ if (const auto *DD = dyn_cast<CXXDestructorDecl>(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 @@ -816,15 +827,22 @@ if (Method->isCopyAssignmentOperator()) { SMKind |= SMF_CopyAssignment; + if (Method->isConsteval() && Method->isDefaulted()) + data().DefaultedSpecialMemberIsConsteval |= SMF_CopyAssignment; + const auto *ParamTy = Method->getParamDecl(0)->getType()->getAs<ReferenceType>(); 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<CXXConversionDecl>(D)) { // FIXME: We use the 'unsafe' accessor for the access specifier here, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -803,6 +803,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; @@ -1062,6 +1067,14 @@ llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs; + /// Set of candidate for starting an immediate invocation. + llvm::SmallVector<llvm::PointerIntPair<ConstantExpr *, 1>, 8> + ImmediateInvocationCandidates; + + /// Set of DeclRefExpr referencing a consteval function when used in a + /// context not already know to be immediately invoked. + llvm::SmallPtrSet<DeclRefExpr *, 8> ReferenceToConsteval; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -5276,6 +5289,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/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<DiagGroup<"constexpr-not-const">>; +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<CXXPre2aCompat>, 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<DiagGroup<"invalid-constexpr">>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; 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/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -465,6 +465,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; @@ -1016,6 +1019,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() {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits