diff options
author | Hans Wennborg <hans@hanshq.net> | 2019-05-13 13:19:09 +0000 |
---|---|---|
committer | Hans Wennborg <hans@hanshq.net> | 2019-05-13 13:19:09 +0000 |
commit | 24e6d2a7b6f041a6df9e61cf9cecfc15c1f7ccac (patch) | |
tree | fc365619cc8ebc19a6743544184f14d4674f3321 | |
parent | f585a602f52c5f1c02c0c39400a2ca273e3a51b1 (diff) | |
download | clang-24e6d2a7b6f041a6df9e61cf9cecfc15c1f7ccac.tar.gz |
Revert r360559 "[c++20] P1064R0: Allow virtual function calls in constant expression evaluation."
This caused Chromium builds to hit the new "can't handle virtual calls with
virtual bases" assert. Reduced repro coming up.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360580 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/DeclCXX.h | 11 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticASTKinds.td | 2 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 13 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 228 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 38 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiate.cpp | 12 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 22 | ||||
-rw-r--r-- | test/CXX/drs/dr18xx.cpp | 14 | ||||
-rw-r--r-- | test/CXX/drs/dr6xx.cpp | 15 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx2a.cpp | 93 | ||||
-rw-r--r-- | test/SemaCXX/cxx17-compat.cpp | 9 | ||||
-rwxr-xr-x | www/cxx_status.html | 2 |
14 files changed, 52 insertions, 414 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index fc243de896..b8a4c1b1b7 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -2298,17 +2298,6 @@ 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) { diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 232987e95c..8801461a7e 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -32,8 +32,6 @@ 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">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 50832bd6bc..4cbaf2e1d3 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2314,9 +2314,6 @@ def err_constexpr_redecl_mismatch : Error< "%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 " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 51302a29b4..90002e9350 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5985,8 +5985,8 @@ public: /// MarkVirtualMembersReferenced - Will mark all members of the given /// CXXRecordDecl referenced. - void MarkVirtualMembersReferenced(SourceLocation Loc, const CXXRecordDecl *RD, - bool ConstexprOnly = false); + void MarkVirtualMembersReferenced(SourceLocation Loc, + const CXXRecordDecl *RD); /// Define all of the vtables that have been used in this /// translation unit and reference any virtual members used by those diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index f9f70ecb59..b9ecdc6572 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1946,8 +1946,8 @@ static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, } CXXMethodDecl * -CXXMethodDecl::getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD, - bool MayBeBase) { +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase) { if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) return this; @@ -1973,15 +1973,6 @@ CXXMethodDecl::getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD, 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) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index adc82bfe0b..ea36072bd9 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -37,7 +37,6 @@ #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" @@ -2486,21 +2485,6 @@ static bool HandleLValueBasePath(EvalInfo &Info, const CastExpr *E, 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, @@ -4477,19 +4461,16 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc, } // DR1872: An instantiated virtual constexpr function can't be called in a - // 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); + // constant expression. + if (isa<CXXMethodDecl>(Declaration) && + cast<CXXMethodDecl>(Declaration)->isVirtual()) { + Info.FFDiag(CallLoc, diag::note_constexpr_virtual_call); return false; } // Can we evaluate this function call? - if (Definition && Definition->isConstexpr() && Body) + if (Definition && Definition->isConstexpr() && + !Definition->isInvalidDecl() && Body) return true; if (Info.getLangOpts().CPlusPlus11) { @@ -4565,153 +4546,6 @@ static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *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) { @@ -4901,6 +4735,11 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, 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; @@ -4961,11 +4800,6 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, 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 && @@ -5206,30 +5040,27 @@ 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 CXXMethodDecl *Member = nullptr; + const ValueDecl *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 = dyn_cast<CXXMethodDecl>(ME->getMemberDecl()); - if (!Member) - return Error(Callee); + Member = ME->getMemberDecl(); This = &ThisVal; - HasQualifier = ME->hasQualifier(); } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) { // Indirect bound member calls ('.*' or '->*'). - Member = dyn_cast_or_null<CXXMethodDecl>( - HandleMemberPointerAccess(Info, BE, ThisVal, false)); - if (!Member) - return Error(Callee); + Member = HandleMemberPointerAccess(Info, BE, ThisVal, false); + if (!Member) return false; This = &ThisVal; } else return Error(Callee); - FD = Member; + + FD = dyn_cast<FunctionDecl>(Member); + if (!FD) + return Error(Callee); } else if (CalleeType->isFunctionPointerType()) { LValue Call; if (!EvaluatePointer(Callee, Call, Info)) @@ -5299,20 +5130,8 @@ public: } else return Error(E); - 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; - } - } + if (This && !checkMemberCallThisPointer(Info, E, *This)) + return false; const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); @@ -5322,11 +5141,6 @@ public: Result, ResultSlot)) return false; - if (!CovariantAdjustmentPath.empty() && - !HandleCovariantReturnAdjustment(Info, E, Result, - CovariantAdjustmentPath)) - return false; - return true; } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1da2ca8ca5..afd50f1438 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1596,9 +1596,6 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { // 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) @@ -1615,25 +1612,21 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { // C++11 [dcl.constexpr]p3: // The definition of a constexpr function shall satisfy the following // constraints: - // - it shall not be virtual; (removed in C++20) + // - it shall not be virtual; const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(NewFD); if (Method && Method->isVirtual()) { - 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; - } + 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; } // - its return type shall be a literal type; @@ -15204,8 +15197,7 @@ void Sema::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc, } void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, - const CXXRecordDecl *RD, - bool ConstexprOnly) { + const CXXRecordDecl *RD) { // Mark all functions which will appear in RD's vtable as used. CXXFinalOverriderMap FinalOverriders; RD->getFinalOverriders(FinalOverriders); @@ -15220,7 +15212,7 @@ void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, // C++ [basic.def.odr]p2: // [...] A virtual member function is used if it is not pure. [...] - if (!Overrider->isPure() && (!ConstexprOnly || Overrider->isConstexpr())) + if (!Overrider->isPure()) MarkFunctionReferenced(Loc, Overrider); } } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 55d1d94bd8..edc281cad6 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -2082,7 +2082,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, 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: @@ -2129,10 +2128,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, 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()) @@ -2225,14 +2220,9 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, Consumer.HandleTagDeclDefinition(Instantiation); // Always emit the vtable for an explicit instantiation definition - // of a polymorphic class template specialization. Otherwise, eagerly - // instantiate only constexpr virtual functions in preparation for their use - // in constant evaluation. + // of a polymorphic class template specialization. if (TSK == TSK_ExplicitInstantiationDefinition) MarkVTableUsed(PointOfInstantiation, Instantiation, true); - else if (MightHaveConstexprVirtualFunctions) - MarkVirtualMembersReferenced(PointOfInstantiation, Instantiation, - /*ConstexprOnly*/ true); } return Instantiation->isInvalidDecl(); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index cc3917093e..ffc408cddb 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -20,10 +20,7 @@ struct Literal { }; struct S { - virtual int ImplicitlyVirtual() const = 0; -#if __cplusplus <= 201703L - // expected-note@-2 {{overridden virtual function}} -#endif + virtual int ImplicitlyVirtual() const = 0; // expected-note {{overridden virtual function}} }; struct SS : S { int ImplicitlyVirtual() const; @@ -35,21 +32,12 @@ struct T : SS, NonLiteral { constexpr T(); constexpr int f() const; - // - 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 + // - 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; } -#if __cplusplus <= 201703L - // expected-error@-2 {{virtual function cannot be constexpr}} -#endif + constexpr int ImplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}} - virtual constexpr int OutOfLineVirtual() const; -#if __cplusplus <= 201703L - // expected-error@-2 {{virtual function cannot be constexpr}} -#endif + virtual constexpr int OutOfLineVirtual() const; // expected-error {{virtual function cannot be constexpr}} // - 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}} diff --git a/test/CXX/drs/dr18xx.cpp b/test/CXX/drs/dr18xx.cpp index 33c0452b6c..5df132c368 100644 --- a/test/CXX/drs/dr18xx.cpp +++ b/test/CXX/drs/dr18xx.cpp @@ -52,19 +52,9 @@ namespace dr1872 { // dr1872: 9 struct Z : virtual X {}; constexpr int x = A<X>().f(); - 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 + constexpr int y = A<Y>().f(); // expected-error {{constant expression}} expected-note {{call to virtual function}} // Note, this is invalid even though it would not use virtual dispatch. - 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 y2 = A<Y>().A<Y>::f(); // expected-error {{constant expression}} expected-note {{call to virtual function}} constexpr int z = A<Z>().f(); // expected-error {{constant expression}} expected-note {{non-literal type}} #endif } diff --git a/test/CXX/drs/dr6xx.cpp b/test/CXX/drs/dr6xx.cpp index 31642dfbb0..318096c299 100644 --- a/test/CXX/drs/dr6xx.cpp +++ b/test/CXX/drs/dr6xx.cpp @@ -479,21 +479,12 @@ namespace dr647 { // dr647: yes // This is partially superseded by dr1358. struct A { constexpr virtual void f() const; - constexpr virtual void g() const {} -#if __cplusplus <= 201703L - // expected-error@-2 {{virtual function cannot be constexpr}} -#endif + constexpr virtual void g() const {} // expected-error {{virtual function cannot be constexpr}} }; - struct X { virtual void f() const; }; -#if __cplusplus <= 201703L - // expected-note@-2 {{overridden}} -#endif + struct X { virtual void f() const; }; // expected-note {{overridden}} struct B : X { - constexpr void f() const {} -#if __cplusplus <= 201703L - // expected-error@-2 {{virtual function cannot be constexpr}} -#endif + constexpr void f() const {} // expected-error {{virtual function cannot be constexpr}} }; struct NonLiteral { NonLiteral() {} }; // expected-note {{not an aggregate and has no constexpr constructors}} diff --git a/test/SemaCXX/constant-expression-cxx2a.cpp b/test/SemaCXX/constant-expression-cxx2a.cpp index c41c1d0481..bb2a4a07dd 100644 --- a/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/test/SemaCXX/constant-expression-cxx2a.cpp @@ -211,96 +211,3 @@ 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}} -} diff --git a/test/SemaCXX/cxx17-compat.cpp b/test/SemaCXX/cxx17-compat.cpp index 5fcec2ab9c..eee9c239d1 100644 --- a/test/SemaCXX/cxx17-compat.cpp +++ b/test/SemaCXX/cxx17-compat.cpp @@ -63,12 +63,3 @@ 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 -}; diff --git a/www/cxx_status.html b/www/cxx_status.html index 84552c2018..5a3cc4fdc1 100755 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -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="svn" align="center">SVN</td> + <td class="none" align="center">No</td> </tr> <tr> <!-- from San Diego --> <td><a href="http://wg21.link/p1002r1">P1002R1</a></td> |