forked from OSchip/llvm-project
[c++20] P1064R0: Allow virtual function calls in constant expression
evaluation. llvm-svn: 360559
This commit is contained in:
parent
f3be557159
commit
5c5be6b2f7
|
@ -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) {
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -2314,6 +2314,9 @@ 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 "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1946,8 +1946,8 @@ static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD,
|
|||
}
|
||||
|
||||
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::getCorrespondingMethodInClass(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)
|
||||
|
|
|
@ -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(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,
|
||||
|
@ -4461,16 +4477,19 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
|||
}
|
||||
|
||||
// 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(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) {
|
||||
|
@ -4732,11 +4898,6 @@ 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;
|
||||
|
@ -4797,6 +4958,11 @@ 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 &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1596,6 +1596,9 @@ 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)
|
||||
|
@ -1612,21 +1615,25 @@ 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;
|
||||
// - 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::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
|
|||
}
|
||||
|
||||
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(SourceLocation Loc,
|
|||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2082,6 +2082,7 @@ 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:
|
||||
|
@ -2128,6 +2129,10 @@ 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())
|
||||
|
@ -2220,9 +2225,14 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
|
|||
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();
|
||||
|
|
|
@ -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}}
|
||||
// - 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; } // 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
|
||||
|
||||
virtual constexpr int OutOfLineVirtual() const; // expected-error {{virtual function cannot be constexpr}}
|
||||
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}}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue