Author: rsmith Date: Mon May 13 16:35:21 2019 New Revision: 360635 URL: http://llvm.org/viewvc/llvm-project?rev=360635&view=rev Log: [c++20] P1064R0: Allow virtual function calls in constant expression evaluation.
This reinstates r360559, reverted in r360580, with a fix to avoid crashing if evaluation-for-overflow mode encounters a virtual call on an object of a class with a virtual base class, and to generally not try to resolve virtual function calls to objects whose (notional) vptrs are not readable. (The standard rules are unclear here, but this seems like a reasonable approach.) Modified: cfe/trunk/include/clang/AST/DeclCXX.h cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/AST/DeclCXX.cpp cfe/trunk/lib/AST/ExprConstant.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp cfe/trunk/test/CXX/drs/dr18xx.cpp cfe/trunk/test/CXX/drs/dr6xx.cpp cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp cfe/trunk/test/SemaCXX/cxx17-compat.cpp cfe/trunk/test/SemaCXX/integer-overflow.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/DeclCXX.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/DeclCXX.h (original) +++ cfe/trunk/include/clang/AST/DeclCXX.h Mon May 13 16:35:21 2019 @@ -2298,6 +2298,17 @@ public: ->getCorrespondingMethodInClass(RD, MayBeBase); } + /// Find if \p RD declares a function that overrides this function, and if so, + /// return it. Does not search base classes. + CXXMethodDecl *getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD, + bool MayBeBase = false); + const CXXMethodDecl * + getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD, + bool MayBeBase = false) const { + return const_cast<CXXMethodDecl *>(this) + ->getCorrespondingMethodDeclaredInClass(RD, MayBeBase); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Mon May 13 16:35:21 2019 @@ -32,6 +32,10 @@ def note_constexpr_no_return : Note< "control reached end of constexpr function">; def note_constexpr_virtual_call : Note< "cannot evaluate call to virtual function in a constant expression">; +def note_constexpr_pure_virtual_call : Note< + "pure virtual function %q0 called">; +def note_constexpr_virtual_out_of_lifetime : Note< + "virtual function called on object '%0' whose dynamic type is not constant">; def note_constexpr_virtual_base : Note< "cannot construct object of type %0 with virtual base class " "in a constant expression">; Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 13 16:35:21 2019 @@ -2314,6 +2314,9 @@ def err_constexpr_redecl_mismatch : Erro "%select{non-constexpr declaration of %0 follows constexpr declaration" "|constexpr declaration of %0 follows non-constexpr declaration}1">; def err_constexpr_virtual : Error<"virtual function cannot be constexpr">; +def warn_cxx17_compat_constexpr_virtual : Warning< + "virtual constexpr functions are incompatible with " + "C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore; def err_constexpr_virtual_base : Error< "constexpr %select{member function|constructor}0 not allowed in " "%select{struct|interface|class}1 with virtual base " Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Mon May 13 16:35:21 2019 @@ -5985,8 +5985,8 @@ public: /// MarkVirtualMembersReferenced - Will mark all members of the given /// CXXRecordDecl referenced. - void MarkVirtualMembersReferenced(SourceLocation Loc, - const CXXRecordDecl *RD); + void MarkVirtualMembersReferenced(SourceLocation Loc, const CXXRecordDecl *RD, + bool ConstexprOnly = false); /// Define all of the vtables that have been used in this /// translation unit and reference any virtual members used by those Modified: cfe/trunk/lib/AST/DeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/lib/AST/DeclCXX.cpp (original) +++ cfe/trunk/lib/AST/DeclCXX.cpp Mon May 13 16:35:21 2019 @@ -1946,8 +1946,8 @@ static bool recursivelyOverrides(const C } CXXMethodDecl * -CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, - bool MayBeBase) { +CXXMethodDecl::getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD, + bool MayBeBase) { if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) return this; @@ -1973,6 +1973,15 @@ CXXMethodDecl::getCorrespondingMethodInC return MD; } + return nullptr; +} + +CXXMethodDecl * +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase) { + if (auto *MD = getCorrespondingMethodDeclaredInClass(RD, MayBeBase)) + return MD; + for (const auto &I : RD->bases()) { const RecordType *RT = I.getType()->getAs<RecordType>(); if (!RT) Modified: cfe/trunk/lib/AST/ExprConstant.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp (original) +++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May 13 16:35:21 2019 @@ -37,6 +37,7 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/Expr.h" #include "clang/AST/OSLog.h" #include "clang/AST/RecordLayout.h" @@ -2485,6 +2486,21 @@ static bool HandleLValueBasePath(EvalInf return true; } +/// Cast an lvalue referring to a derived class to a known base subobject. +static bool CastToBaseClass(EvalInfo &Info, const Expr *E, LValue &Result, + const CXXRecordDecl *DerivedRD, + const CXXRecordDecl *BaseRD) { + CXXBasePaths Paths(/*FindAmbiguities=*/false, + /*RecordPaths=*/true, /*DetectVirtual=*/false); + if (!DerivedRD->isDerivedFrom(BaseRD, Paths)) + llvm_unreachable("Class must be derived from the passed in base class!"); + + for (CXXBasePathElement &Elem : Paths.front()) + if (!HandleLValueBase(Info, E, Result, Elem.Class, Elem.Base)) + return false; + return true; +} + /// Update LVal to refer to the given field, which must be a member of the type /// currently described by LVal. static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, @@ -4461,16 +4477,19 @@ static bool CheckConstexprFunction(EvalI } // DR1872: An instantiated virtual constexpr function can't be called in a - // constant expression. - if (isa<CXXMethodDecl>(Declaration) && - cast<CXXMethodDecl>(Declaration)->isVirtual()) { - Info.FFDiag(CallLoc, diag::note_constexpr_virtual_call); + // constant expression (prior to C++20). We can still constant-fold such a + // call. + if (!Info.Ctx.getLangOpts().CPlusPlus2a && isa<CXXMethodDecl>(Declaration) && + cast<CXXMethodDecl>(Declaration)->isVirtual()) + Info.CCEDiag(CallLoc, diag::note_constexpr_virtual_call); + + if (Definition && Definition->isInvalidDecl()) { + Info.FFDiag(CallLoc, diag::note_invalid_subexpr_in_const_expr); return false; } // Can we evaluate this function call? - if (Definition && Definition->isConstexpr() && - !Definition->isInvalidDecl() && Body) + if (Definition && Definition->isConstexpr() && Body) return true; if (Info.getLangOpts().CPlusPlus11) { @@ -4517,7 +4536,7 @@ const AccessKinds CheckMemberCallThisPoi /// Check that the pointee of the 'this' pointer in a member function call is /// either within its lifetime or in its period of construction or destruction. static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *E, - const LValue &This) { + const LValue &This, bool IsVirtual) { if (This.Designator.Invalid) return false; @@ -4538,6 +4557,16 @@ static bool checkMemberCallThisPointer(E : diag::note_constexpr_access_unsized_array) << AK_MemberCall; return false; + } else if (IsVirtual) { + // Conservatively refuse to perform a virtual function call if we would + // not be able to read a notional 'vptr' value. + APValue Val; + This.moveInto(Val); + QualType StarThisType = + Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx)); + Info.FFDiag(E, diag::note_constexpr_virtual_out_of_lifetime) + << Val.getAsString(Info.Ctx, StarThisType); + return false; } return true; } @@ -4546,6 +4575,155 @@ static bool checkMemberCallThisPointer(E return Obj && findSubobject(Info, E, Obj, This.Designator, Handler); } +struct DynamicType { + /// The dynamic class type of the object. + const CXXRecordDecl *Type; + /// The corresponding path length in the lvalue. + unsigned PathLength; +}; + +static const CXXRecordDecl *getBaseClassType(SubobjectDesignator &Designator, + unsigned PathLength) { + assert(PathLength >= Designator.MostDerivedPathLength && PathLength <= + Designator.Entries.size() && "invalid path length"); + return (PathLength == Designator.MostDerivedPathLength) + ? Designator.MostDerivedType->getAsCXXRecordDecl() + : getAsBaseClass(Designator.Entries[PathLength - 1]); +} + +/// Determine the dynamic type of an object. +static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, LValue &This) { + // If we don't have an lvalue denoting an object of class type, there is no + // meaningful dynamic type. (We consider objects of non-class type to have no + // dynamic type.) + if (This.Designator.IsOnePastTheEnd || This.Designator.Invalid || + !This.Designator.MostDerivedType->getAsCXXRecordDecl()) + return None; + + // FIXME: For very deep class hierarchies, it might be beneficial to use a + // binary search here instead. But the overwhelmingly common case is that + // we're not in the middle of a constructor, so it probably doesn't matter + // in practice. + ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries; + for (unsigned PathLength = This.Designator.MostDerivedPathLength; + PathLength <= Path.size(); ++PathLength) { + switch (Info.isEvaluatingConstructor(This.getLValueBase(), + Path.slice(0, PathLength))) { + case ConstructionPhase::Bases: + // We're constructing a base class. This is not the dynamic type. + break; + + case ConstructionPhase::None: + case ConstructionPhase::AfterBases: + // We've finished constructing the base classes, so this is the dynamic + // type. + return DynamicType{getBaseClassType(This.Designator, PathLength), + PathLength}; + } + } + + // CWG issue 1517: we're constructing a base class of the object described by + // 'This', so that object has not yet begun its period of construction and + // any polymorphic operation on it results in undefined behavior. + return None; +} + +/// Perform virtual dispatch. +static const CXXMethodDecl *HandleVirtualDispatch( + EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found, + llvm::SmallVectorImpl<QualType> &CovariantAdjustmentPath) { + Optional<DynamicType> DynType = ComputeDynamicType(Info, This); + if (!DynType) { + Info.FFDiag(E); + return nullptr; + } + + // Find the final overrider. It must be declared in one of the classes on the + // path from the dynamic type to the static type. + // FIXME: If we ever allow literal types to have virtual base classes, that + // won't be true. + const CXXMethodDecl *Callee = Found; + unsigned PathLength = DynType->PathLength; + for (/**/; PathLength <= This.Designator.Entries.size(); ++PathLength) { + const CXXRecordDecl *Class = getBaseClassType(This.Designator, PathLength); + if (Class->getNumVBases()) { + Info.FFDiag(E); + return nullptr; + } + + const CXXMethodDecl *Overrider = + Found->getCorrespondingMethodDeclaredInClass(Class, false); + if (Overrider) { + Callee = Overrider; + break; + } + } + + // C++2a [class.abstract]p6: + // the effect of making a virtual call to a pure virtual function [...] is + // undefined + if (Callee->isPure()) { + Info.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << Callee; + Info.Note(Callee->getLocation(), diag::note_declared_at); + return nullptr; + } + + // If necessary, walk the rest of the path to determine the sequence of + // covariant adjustment steps to apply. + if (!Info.Ctx.hasSameUnqualifiedType(Callee->getReturnType(), + Found->getReturnType())) { + CovariantAdjustmentPath.push_back(Callee->getReturnType()); + for (unsigned CovariantPathLength = PathLength + 1; + CovariantPathLength != This.Designator.Entries.size(); + ++CovariantPathLength) { + const CXXRecordDecl *NextClass = + getBaseClassType(This.Designator, CovariantPathLength); + const CXXMethodDecl *Next = + Found->getCorrespondingMethodDeclaredInClass(NextClass, false); + if (Next && !Info.Ctx.hasSameUnqualifiedType( + Next->getReturnType(), CovariantAdjustmentPath.back())) + CovariantAdjustmentPath.push_back(Next->getReturnType()); + } + if (!Info.Ctx.hasSameUnqualifiedType(Found->getReturnType(), + CovariantAdjustmentPath.back())) + CovariantAdjustmentPath.push_back(Found->getReturnType()); + } + + // Perform 'this' adjustment. + if (!CastToDerivedClass(Info, E, This, Callee->getParent(), PathLength)) + return nullptr; + + return Callee; +} + +/// Perform the adjustment from a value returned by a virtual function to +/// a value of the statically expected type, which may be a pointer or +/// reference to a base class of the returned type. +static bool HandleCovariantReturnAdjustment(EvalInfo &Info, const Expr *E, + APValue &Result, + ArrayRef<QualType> Path) { + assert(Result.isLValue() && + "unexpected kind of APValue for covariant return"); + if (Result.isNullPointer()) + return true; + + LValue LVal; + LVal.setFrom(Info.Ctx, Result); + + const CXXRecordDecl *OldClass = Path[0]->getPointeeCXXRecordDecl(); + for (unsigned I = 1; I != Path.size(); ++I) { + const CXXRecordDecl *NewClass = Path[I]->getPointeeCXXRecordDecl(); + assert(OldClass && NewClass && "unexpected kind of covariant return"); + if (OldClass != NewClass && + !CastToBaseClass(Info, E, LVal, OldClass, NewClass)) + return false; + OldClass = NewClass; + } + + LVal.moveInto(Result); + return true; +} + /// Determine if a class has any fields that might need to be copied by a /// trivial copy or move operation. static bool hasFields(const CXXRecordDecl *RD) { @@ -4735,11 +4913,6 @@ static bool HandleConstructorCall(const BaseType->getAsCXXRecordDecl(), &Layout)) return false; Value = &Result.getStructBase(BasesSeen++); - - // This is the point at which the dynamic type of the object becomes this - // class type. - if (BasesSeen == RD->getNumBases()) - EvalObj.finishedConstructingBases(); } else if ((FD = I->getMember())) { if (!HandleLValueMember(Info, I->getInit(), Subobject, FD, &Layout)) return false; @@ -4800,6 +4973,11 @@ static bool HandleConstructorCall(const return false; Success = false; } + + // This is the point at which the dynamic type of the object becomes this + // class type. + if (I->isBaseInitializer() && BasesSeen == RD->getNumBases()) + EvalObj.finishedConstructingBases(); } return Success && @@ -5040,27 +5218,30 @@ public: const FunctionDecl *FD = nullptr; LValue *This = nullptr, ThisVal; auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs()); + bool HasQualifier = false; // Extract function decl and 'this' pointer from the callee. if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) { - const ValueDecl *Member = nullptr; + const CXXMethodDecl *Member = nullptr; if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) { // Explicit bound member calls, such as x.f() or p->g(); if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal)) return false; - Member = ME->getMemberDecl(); + Member = dyn_cast<CXXMethodDecl>(ME->getMemberDecl()); + if (!Member) + return Error(Callee); This = &ThisVal; + HasQualifier = ME->hasQualifier(); } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) { // Indirect bound member calls ('.*' or '->*'). - Member = HandleMemberPointerAccess(Info, BE, ThisVal, false); - if (!Member) return false; + Member = dyn_cast_or_null<CXXMethodDecl>( + HandleMemberPointerAccess(Info, BE, ThisVal, false)); + if (!Member) + return Error(Callee); This = &ThisVal; } else return Error(Callee); - - FD = dyn_cast<FunctionDecl>(Member); - if (!FD) - return Error(Callee); + FD = Member; } else if (CalleeType->isFunctionPointerType()) { LValue Call; if (!EvaluatePointer(Callee, Call, Info)) @@ -5130,8 +5311,20 @@ public: } else return Error(E); - if (This && !checkMemberCallThisPointer(Info, E, *This)) - return false; + SmallVector<QualType, 4> CovariantAdjustmentPath; + if (This) { + auto *NamedMember = dyn_cast<CXXMethodDecl>(FD); + bool IsVirtual = NamedMember && NamedMember->isVirtual() && !HasQualifier; + + // Check that the 'this' pointer points to an object of the right type. + if (!checkMemberCallThisPointer(Info, E, *This, IsVirtual)) + return false; + + // Perform virtual dispatch, if necessary. + if (IsVirtual && !(FD = HandleVirtualDispatch(Info, E, *This, NamedMember, + CovariantAdjustmentPath))) + return true; + } const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); @@ -5141,6 +5334,11 @@ public: Result, ResultSlot)) return false; + if (!CovariantAdjustmentPath.empty() && + !HandleCovariantReturnAdjustment(Info, E, Result, + CovariantAdjustmentPath)) + return false; + return true; } Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May 13 16:35:21 2019 @@ -1596,6 +1596,9 @@ bool Sema::CheckConstexprFunctionDecl(co // The definition of a constexpr constructor shall satisfy the following // constraints: // - the class shall not have any virtual base classes; + // + // FIXME: This only applies to constructors, not arbitrary member + // functions. const CXXRecordDecl *RD = MD->getParent(); if (RD->getNumVBases()) { Diag(NewFD->getLocation(), diag::err_constexpr_virtual_base) @@ -1612,21 +1615,25 @@ bool Sema::CheckConstexprFunctionDecl(co // C++11 [dcl.constexpr]p3: // The definition of a constexpr function shall satisfy the following // constraints: - // - it shall not be virtual; + // - it shall not be virtual; (removed in C++20) const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(NewFD); if (Method && Method->isVirtual()) { - Method = Method->getCanonicalDecl(); - Diag(Method->getLocation(), diag::err_constexpr_virtual); + if (getLangOpts().CPlusPlus2a) { + Diag(Method->getLocation(), diag::warn_cxx17_compat_constexpr_virtual); + } else { + Method = Method->getCanonicalDecl(); + Diag(Method->getLocation(), diag::err_constexpr_virtual); - // If it's not obvious why this function is virtual, find an overridden - // function which uses the 'virtual' keyword. - const CXXMethodDecl *WrittenVirtual = Method; - while (!WrittenVirtual->isVirtualAsWritten()) - WrittenVirtual = *WrittenVirtual->begin_overridden_methods(); - if (WrittenVirtual != Method) - Diag(WrittenVirtual->getLocation(), - diag::note_overridden_virtual_function); - return false; + // If it's not obvious why this function is virtual, find an overridden + // function which uses the 'virtual' keyword. + const CXXMethodDecl *WrittenVirtual = Method; + while (!WrittenVirtual->isVirtualAsWritten()) + WrittenVirtual = *WrittenVirtual->begin_overridden_methods(); + if (WrittenVirtual != Method) + Diag(WrittenVirtual->getLocation(), + diag::note_overridden_virtual_function); + return false; + } } // - its return type shall be a literal type; @@ -15197,7 +15204,8 @@ void Sema::MarkVirtualMemberExceptionSpe } void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, - const CXXRecordDecl *RD) { + const CXXRecordDecl *RD, + bool ConstexprOnly) { // Mark all functions which will appear in RD's vtable as used. CXXFinalOverriderMap FinalOverriders; RD->getFinalOverriders(FinalOverriders); @@ -15212,7 +15220,7 @@ void Sema::MarkVirtualMembersReferenced( // C++ [basic.def.odr]p2: // [...] A virtual member function is used if it is not pure. [...] - if (!Overrider->isPure()) + if (!Overrider->isPure() && (!ConstexprOnly || Overrider->isConstexpr())) MarkFunctionReferenced(Loc, Overrider); } } Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Mon May 13 16:35:21 2019 @@ -2082,6 +2082,7 @@ Sema::InstantiateClass(SourceLocation Po LateInstantiatedAttrVec LateAttrs; Instantiator.enableLateAttributeInstantiation(&LateAttrs); + bool MightHaveConstexprVirtualFunctions = false; for (auto *Member : Pattern->decls()) { // Don't instantiate members not belonging in this semantic context. // e.g. for: @@ -2128,6 +2129,10 @@ Sema::InstantiateClass(SourceLocation Po Instantiation->setInvalidDecl(); break; } + } else if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewMember)) { + if (MD->isConstexpr() && !MD->getFriendObjectKind() && + (MD->isVirtualAsWritten() || Instantiation->getNumBases())) + MightHaveConstexprVirtualFunctions = true; } if (NewMember->isInvalidDecl()) @@ -2220,9 +2225,14 @@ Sema::InstantiateClass(SourceLocation Po Consumer.HandleTagDeclDefinition(Instantiation); // Always emit the vtable for an explicit instantiation definition - // of a polymorphic class template specialization. + // of a polymorphic class template specialization. Otherwise, eagerly + // instantiate only constexpr virtual functions in preparation for their use + // in constant evaluation. if (TSK == TSK_ExplicitInstantiationDefinition) MarkVTableUsed(PointOfInstantiation, Instantiation, true); + else if (MightHaveConstexprVirtualFunctions) + MarkVirtualMembersReferenced(PointOfInstantiation, Instantiation, + /*ConstexprOnly*/ true); } return Instantiation->isInvalidDecl(); Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp Mon May 13 16:35:21 2019 @@ -20,7 +20,10 @@ struct Literal { }; struct S { - virtual int ImplicitlyVirtual() const = 0; // expected-note {{overridden virtual function}} + virtual int ImplicitlyVirtual() const = 0; +#if __cplusplus <= 201703L + // expected-note@-2 {{overridden virtual function}} +#endif }; struct SS : S { int ImplicitlyVirtual() const; @@ -32,12 +35,21 @@ struct T : SS, NonLiteral { constexpr T(); constexpr int f() const; - // - it shall not be virtual; - virtual constexpr int ExplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}} - - constexpr int ImplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}} - - virtual constexpr int OutOfLineVirtual() const; // expected-error {{virtual function cannot be constexpr}} + // - it shall not be virtual; [until C++20] + virtual constexpr int ExplicitlyVirtual() const { return 0; } +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#endif + + constexpr int ImplicitlyVirtual() const { return 0; } +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#endif + + virtual constexpr int OutOfLineVirtual() const; +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#endif // - its return type shall be a literal type; constexpr NonLiteral NonLiteralReturn() const { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}} Modified: cfe/trunk/test/CXX/drs/dr18xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr18xx.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr18xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr18xx.cpp Mon May 13 16:35:21 2019 @@ -52,9 +52,19 @@ namespace dr1872 { // dr1872: 9 struct Z : virtual X {}; constexpr int x = A<X>().f(); - constexpr int y = A<Y>().f(); // expected-error {{constant expression}} expected-note {{call to virtual function}} + constexpr int y = A<Y>().f(); +#if __cplusplus <= 201703L + // expected-error@-2 {{constant expression}} expected-note@-2 {{call to virtual function}} +#else + static_assert(y == 0); +#endif // Note, this is invalid even though it would not use virtual dispatch. - constexpr int y2 = A<Y>().A<Y>::f(); // expected-error {{constant expression}} expected-note {{call to virtual function}} + constexpr int y2 = A<Y>().A<Y>::f(); +#if __cplusplus <= 201703L + // expected-error@-2 {{constant expression}} expected-note@-2 {{call to virtual function}} +#else + static_assert(y == 0); +#endif constexpr int z = A<Z>().f(); // expected-error {{constant expression}} expected-note {{non-literal type}} #endif } Modified: cfe/trunk/test/CXX/drs/dr6xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr6xx.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr6xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr6xx.cpp Mon May 13 16:35:21 2019 @@ -479,12 +479,21 @@ namespace dr647 { // dr647: yes // This is partially superseded by dr1358. struct A { constexpr virtual void f() const; - constexpr virtual void g() const {} // expected-error {{virtual function cannot be constexpr}} + constexpr virtual void g() const {} +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#endif }; - struct X { virtual void f() const; }; // expected-note {{overridden}} + struct X { virtual void f() const; }; +#if __cplusplus <= 201703L + // expected-note@-2 {{overridden}} +#endif struct B : X { - constexpr void f() const {} // expected-error {{virtual function cannot be constexpr}} + constexpr void f() const {} +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#endif }; struct NonLiteral { NonLiteral() {} }; // expected-note {{not an aggregate and has no constexpr constructors}} Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Mon May 13 16:35:21 2019 @@ -211,3 +211,94 @@ constexpr bool for_range_init() { return k == 6; } static_assert(for_range_init()); + +namespace Virtual { + struct NonZeroOffset { int padding = 123; }; + + // Ensure that we pick the right final overrider during construction. + struct A { + virtual constexpr char f() const { return 'A'; } + char a = f(); + }; + struct NoOverrideA : A {}; + struct B : NonZeroOffset, NoOverrideA { + virtual constexpr char f() const { return 'B'; } + char b = f(); + }; + struct NoOverrideB : B {}; + struct C : NonZeroOffset, A { + virtual constexpr char f() const { return 'C'; } + A *pba; + char c = ((A*)this)->f(); + char ba = pba->f(); + constexpr C(A *pba) : pba(pba) {} + }; + struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}} + virtual constexpr char f() const { return 'D'; } + char d = f(); + constexpr D() : C((B*)this) {} + }; + constexpr D d; + static_assert(((B&)d).a == 'A'); + static_assert(((C&)d).a == 'A'); + static_assert(d.b == 'B'); + static_assert(d.c == 'C'); + // During the construction of C, the dynamic type of B's A is B. + static_assert(d.ba == 'B'); + static_assert(d.d == 'D'); + static_assert(d.f() == 'D'); + constexpr const A &a = (B&)d; + constexpr const B &b = d; + static_assert(a.f() == 'D'); + static_assert(b.f() == 'D'); + + // FIXME: It is unclear whether this should be permitted. + D d_not_constexpr; + static_assert(d_not_constexpr.f() == 'D'); // expected-error {{constant expression}} expected-note {{virtual function called on object 'd_not_constexpr' whose dynamic type is not constant}} + + // Check that we apply a proper adjustment for a covariant return type. + struct Covariant1 { + D d; + virtual const A *f() const; + }; + template<typename T> + struct Covariant2 : Covariant1 { + virtual const T *f() const; + }; + template<typename T> + struct Covariant3 : Covariant2<T> { + constexpr virtual const D *f() const { return &this->d; } + }; + + constexpr Covariant3<B> cb; + constexpr Covariant3<C> cc; + + constexpr const Covariant1 *cb1 = &cb; + constexpr const Covariant2<B> *cb2 = &cb; + static_assert(cb1->f()->a == 'A'); + static_assert(cb1->f() == (B*)&cb.d); + static_assert(cb1->f()->f() == 'D'); + static_assert(cb2->f()->b == 'B'); + static_assert(cb2->f() == &cb.d); + static_assert(cb2->f()->f() == 'D'); + + constexpr const Covariant1 *cc1 = &cc; + constexpr const Covariant2<C> *cc2 = &cc; + static_assert(cc1->f()->a == 'A'); + static_assert(cc1->f() == (C*)&cc.d); + static_assert(cc1->f()->f() == 'D'); + static_assert(cc2->f()->c == 'C'); + static_assert(cc2->f() == &cc.d); + static_assert(cc2->f()->f() == 'D'); + + static_assert(cb.f()->d == 'D'); + static_assert(cc.f()->d == 'D'); + + struct Abstract { + constexpr virtual void f() = 0; // expected-note {{declared here}} + constexpr Abstract() { do_it(); } // expected-note {{in call to}} + constexpr void do_it() { f(); } // expected-note {{pure virtual function 'Virtual::Abstract::f' called}} + }; + struct PureVirtualCall : Abstract { void f(); }; // expected-note {{in call to 'Abstract}} + constexpr PureVirtualCall pure_virtual_call; // expected-error {{constant expression}} expected-note {{in call to 'PureVirtualCall}} +} Modified: cfe/trunk/test/SemaCXX/cxx17-compat.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx17-compat.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx17-compat.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx17-compat.cpp Mon May 13 16:35:21 2019 @@ -63,3 +63,12 @@ void ForRangeInit() { // expected-warning@-4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}} #endif } + +struct ConstexprVirtual { + virtual constexpr void f() {} +#if __cplusplus <= 201703L + // expected-error@-2 {{virtual function cannot be constexpr}} +#else + // expected-warning@-4 {{virtual constexpr functions are incompatible with C++ standards before C++2a}} +#endif +}; Modified: cfe/trunk/test/SemaCXX/integer-overflow.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/integer-overflow.cpp?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/integer-overflow.cpp (original) +++ cfe/trunk/test/SemaCXX/integer-overflow.cpp Mon May 13 16:35:21 2019 @@ -1,4 +1,6 @@ // RUN: %clang_cc1 %s -verify -fsyntax-only -std=gnu++98 -triple x86_64-pc-linux-gnu +// RUN: %clang_cc1 %s -verify -fsyntax-only -std=gnu++2a -triple x86_64-pc-linux-gnu + typedef unsigned long long uint64_t; typedef unsigned int uint32_t; @@ -13,7 +15,7 @@ uint64_t f2(uint64_t, ...); static const uint64_t overflow = 1 * 4608 * 1024 * 1024; // expected-warning {{overflow in expression; result is 536870912 with type 'int'}} -uint64_t check_integer_overflows(int i) { //expected-note {{declared here}} +uint64_t check_integer_overflows(int i) { //expected-note 0+{{declared here}} // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} uint64_t overflow = 4608 * 1024 * 1024, // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} @@ -72,6 +74,7 @@ uint64_t check_integer_overflows(int i) if ((uint64_t)((uint64_t)(4608 * 1024 * 1024) * (uint64_t)(4608 * 1024 * 1024))) return 5; +#if __cplusplus < 201103L switch (i) { // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} case 4608 * 1024 * 1024: @@ -94,6 +97,7 @@ uint64_t check_integer_overflows(int i) case ((uint64_t)((uint64_t)(4608 * 1024 * 1024) * (uint64_t)(4608 * 1024 * 1024))): return 10; } +#endif // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} while (4608 * 1024 * 1024); @@ -160,11 +164,13 @@ uint64_t check_integer_overflows(int i) // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} (__imag__ x) = 4608 * 1024 * 1024; -// expected-warning@+4 {{overflow in expression; result is 536870912 with type 'int'}} -// expected-warning@+3 {{array index 536870912 is past the end of the array (which contains 10 elements)}} -// expected-note@+1 {{array 'a' declared here}} +// expected-warning@+2 {{overflow in expression; result is 536870912 with type 'int'}} uint64_t a[10]; a[4608 * 1024 * 1024] = 1; +#if __cplusplus < 201103L +// expected-warning@-2 {{array index 536870912 is past the end of the array (which contains 10 elements)}} +// expected-note@-4 {{array 'a' declared here}} +#endif // expected-warning@+1 2{{overflow in expression; result is 536870912 with type 'int'}} return ((4608 * 1024 * 1024) + ((uint64_t)(4608 * 1024 * 1024))); @@ -184,3 +190,22 @@ void check_integer_overflows_in_function // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} (void)f2(0, f0(4608 * 1024 * 1024)); } + +// Tests that ensure that evaluation-for-overflow of random expressions doesn't +// crash. +namespace EvaluationCrashes { + namespace VirtualCallWithVbase { + struct A {}; + struct B : virtual A { + virtual bool f(const void *, int); + }; + struct C : B { + bool f(const void *, int); + }; + int d; + bool e(C c) { + if (c.f(&d, d)) {} + return true; + } + } +} Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=360635&r1=360634&r2=360635&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Mon May 13 16:35:21 2019 @@ -965,7 +965,7 @@ as the draft C++2a standard evolves. <tr> <td rowspan=4>Relaxations of <tt>constexpr</tt> restrictions</td> <td><a href="http://wg21.link/p1064r0">P1064R0</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <!-- from San Diego --> <td><a href="http://wg21.link/p1002r1">P1002R1</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits