242 changes: 220 additions & 22 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -4517,7 +4536,7 @@ const AccessKinds CheckMemberCallThisPointerHandler::AccessKind;
/// 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;

Expand All @@ -4538,6 +4557,16 @@ static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *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;
}
Expand All @@ -4546,6 +4575,155 @@ 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);
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) {
Expand Down Expand Up @@ -4735,11 +4913,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;
Expand Down Expand Up @@ -4800,6 +4973,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 &&
Expand Down Expand Up @@ -5040,27 +5218,30 @@ class ExprEvaluatorBase
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))
Expand Down Expand Up @@ -5130,8 +5311,20 @@ class ExprEvaluatorBase
} 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);
Expand All @@ -5141,6 +5334,11 @@ class ExprEvaluatorBase
Result, ResultSlot))
return false;

if (!CovariantAdjustmentPath.empty() &&
!HandleCovariantReturnAdjustment(Info, E, Result,
CovariantAdjustmentPath))
return false;

return true;
}

Expand Down
38 changes: 23 additions & 15 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 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 (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;
}
}

// - its return type shall be a literal type;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
}
Expand Down
12 changes: 11 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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();
Expand Down
22 changes: 17 additions & 5 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}}
Expand Down
14 changes: 12 additions & 2 deletions clang/test/CXX/drs/dr18xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
15 changes: 12 additions & 3 deletions clang/test/CXX/drs/dr6xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
91 changes: 91 additions & 0 deletions clang/test/SemaCXX/constant-expression-cxx2a.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
}
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/cxx17-compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
33 changes: 29 additions & 4 deletions clang/test/SemaCXX/integer-overflow.cpp
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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'}}
Expand Down Expand Up @@ -72,6 +74,7 @@ uint64_t check_integer_overflows(int i) { //expected-note {{declared here}}
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:
Expand All @@ -94,6 +97,7 @@ uint64_t check_integer_overflows(int i) { //expected-note {{declared here}}
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);
Expand Down Expand Up @@ -160,11 +164,13 @@ uint64_t check_integer_overflows(int i) { //expected-note {{declared here}}
// 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)));
Expand All @@ -184,3 +190,22 @@ void check_integer_overflows_in_function_calls() {
// 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;
}
}
}
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ <h2 id="cxx20">C++2a implementation status</h2>
<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>
Expand Down