Author: rsmith Date: Mon May 13 00:42:10 2019 New Revision: 360559 URL: http://llvm.org/viewvc/llvm-project?rev=360559&view=rev Log: [c++20] P1064R0: Allow virtual function calls in constant expression evaluation.
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/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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/DeclCXX.h (original) +++ cfe/trunk/include/clang/AST/DeclCXX.h Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Mon May 13 00:42:10 2019 @@ -32,6 +32,8 @@ 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_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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Mon May 13 00:42:10 2019 @@ -5984,8 +5984,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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/lib/AST/DeclCXX.cpp (original) +++ cfe/trunk/lib/AST/DeclCXX.cpp Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp (original) +++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May 13 00:42:10 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) { @@ -4543,6 +4562,153 @@ 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); + assert(!Class->getNumVBases() && + "can't handle virtual calls with virtual bases"); + + 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) { @@ -4732,11 +4898,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; @@ -4797,6 +4958,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 && @@ -5037,27 +5203,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)) @@ -5127,8 +5296,20 @@ public: } else return Error(E); - if (This && !checkMemberCallThisPointer(Info, E, *This)) - return false; + SmallVector<QualType, 4> CovariantAdjustmentPath; + if (This) { + // Check that the 'this' pointer points to an object of the right type. + if (!checkMemberCallThisPointer(Info, E, *This)) + return false; + + // Perform virtual dispatch, if necessary. + auto *NamedMember = dyn_cast<CXXMethodDecl>(FD); + if (NamedMember && NamedMember->isVirtual() && !HasQualifier) { + if (!(FD = HandleVirtualDispatch(Info, E, *This, NamedMember, + CovariantAdjustmentPath))) + return true; + } + } const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); @@ -5138,6 +5319,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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&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 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr18xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr18xx.cpp Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr6xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr6xx.cpp Mon May 13 00:42:10 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Mon May 13 00:42:10 2019 @@ -211,3 +211,96 @@ 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. We assume that + // objects whose values are not known within evaluation are within their + // lifetimes. + D d_not_constexpr; + static_assert(d_not_constexpr.f() == 'D'); + + // 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=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx17-compat.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx17-compat.cpp Mon May 13 00:42:10 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/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=360559&r1=360558&r2=360559&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Mon May 13 00:42:10 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