forked from OSchip/llvm-project
Fix PR13052 properly, by performing special member lookup to determine whether
an explicitly-defaulted default constructor would be constexpr. This is necessary in weird (but well-formed) cases where a class has more than one copy or move constructor. Cleanup of now-unused parts of CXXRecordDecl to follow. llvm-svn: 158289
This commit is contained in:
parent
2b013185f8
commit
b58000950e
|
@ -3870,6 +3870,109 @@ void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) {
|
|||
CheckExplicitlyDefaultedSpecialMember(*MI);
|
||||
}
|
||||
|
||||
/// Is the special member function which would be selected to perform the
|
||||
/// specified operation on the specified class type a constexpr constructor?
|
||||
static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
|
||||
Sema::CXXSpecialMember CSM,
|
||||
bool ConstArg) {
|
||||
Sema::SpecialMemberOverloadResult *SMOR =
|
||||
S.LookupSpecialMember(ClassDecl, CSM, ConstArg,
|
||||
false, false, false, false);
|
||||
if (!SMOR || !SMOR->getMethod())
|
||||
// A constructor we wouldn't select can't be "involved in initializing"
|
||||
// anything.
|
||||
return true;
|
||||
return SMOR->getMethod()->isConstexpr();
|
||||
}
|
||||
|
||||
/// Determine whether the specified special member function would be constexpr
|
||||
/// if it were implicitly defined.
|
||||
static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
|
||||
Sema::CXXSpecialMember CSM,
|
||||
bool ConstArg) {
|
||||
if (!S.getLangOpts().CPlusPlus0x)
|
||||
return false;
|
||||
|
||||
// C++11 [dcl.constexpr]p4:
|
||||
// In the definition of a constexpr constructor [...]
|
||||
switch (CSM) {
|
||||
case Sema::CXXDefaultConstructor:
|
||||
case Sema::CXXCopyConstructor:
|
||||
case Sema::CXXMoveConstructor:
|
||||
break;
|
||||
|
||||
case Sema::CXXCopyAssignment:
|
||||
case Sema::CXXMoveAssignment:
|
||||
case Sema::CXXDestructor:
|
||||
case Sema::CXXInvalid:
|
||||
return false;
|
||||
}
|
||||
|
||||
// -- if the class is a non-empty union, or for each non-empty anonymous
|
||||
// union member of a non-union class, exactly one non-static data member
|
||||
// shall be initialized; [DR1359]
|
||||
if (ClassDecl->isUnion())
|
||||
// FIXME: In the default constructor case, we should check that the
|
||||
// in-class initializer is actually a constant expression.
|
||||
return CSM != Sema::CXXDefaultConstructor ||
|
||||
ClassDecl->hasInClassInitializer();
|
||||
|
||||
// -- the class shall not have any virtual base classes;
|
||||
if (ClassDecl->getNumVBases())
|
||||
return false;
|
||||
|
||||
// -- every constructor involved in initializing [...] base class
|
||||
// sub-objects shall be a constexpr constructor;
|
||||
for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
|
||||
BEnd = ClassDecl->bases_end();
|
||||
B != BEnd; ++B) {
|
||||
const RecordType *BaseType = B->getType()->getAs<RecordType>();
|
||||
if (!BaseType) continue;
|
||||
|
||||
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
|
||||
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, ConstArg))
|
||||
return false;
|
||||
}
|
||||
|
||||
// -- every constructor involved in initializing non-static data members
|
||||
// [...] shall be a constexpr constructor;
|
||||
// -- every non-static data member and base class sub-object shall be
|
||||
// initialized
|
||||
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
|
||||
FEnd = ClassDecl->field_end();
|
||||
F != FEnd; ++F) {
|
||||
if (F->isInvalidDecl())
|
||||
continue;
|
||||
if (CSM == Sema::CXXDefaultConstructor && F->hasInClassInitializer()) {
|
||||
// -- every assignment-expression that is an initializer-clause appearing
|
||||
// directly or indirectly within a brace-or-equal-initializer for a
|
||||
// non-static data member [...] shall be a constant expression;
|
||||
//
|
||||
// We consider this bullet to be a defect, since it results in this type
|
||||
// having a non-constexpr default constructor:
|
||||
// struct S {
|
||||
// int a = 0;
|
||||
// int b = a;
|
||||
// };
|
||||
// FIXME: We should still check that the constructor selected for this
|
||||
// initialization (if any) is constexpr.
|
||||
} else if (const RecordType *RecordTy =
|
||||
S.Context.getBaseElementType(F->getType())->
|
||||
getAs<RecordType>()) {
|
||||
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
|
||||
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM, ConstArg))
|
||||
return false;
|
||||
} else if (CSM == Sema::CXXDefaultConstructor) {
|
||||
// No in-class initializer, and not a class type. This member isn't going
|
||||
// to be initialized.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All OK, it's constexpr!
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
||||
CXXRecordDecl *RD = MD->getParent();
|
||||
CXXSpecialMember CSM = getSpecialMember(MD);
|
||||
|
@ -3907,8 +4010,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
// Compute implicit exception specification, argument constness, constexpr
|
||||
// and triviality.
|
||||
ImplicitExceptionSpecification Spec(*this);
|
||||
bool Const = false;
|
||||
bool Constexpr = false;
|
||||
bool CanHaveConstParam = false;
|
||||
bool Trivial;
|
||||
switch (CSM) {
|
||||
case CXXDefaultConstructor:
|
||||
|
@ -3917,23 +4019,20 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
// Exception specification depends on some deferred part of the class.
|
||||
// We'll try again when the class's definition has been fully processed.
|
||||
return;
|
||||
Constexpr = RD->defaultedDefaultConstructorIsConstexpr();
|
||||
Trivial = RD->hasTrivialDefaultConstructor();
|
||||
break;
|
||||
case CXXCopyConstructor:
|
||||
llvm::tie(Spec, Const) =
|
||||
llvm::tie(Spec, CanHaveConstParam) =
|
||||
ComputeDefaultedCopyCtorExceptionSpecAndConst(RD);
|
||||
Constexpr = RD->defaultedCopyConstructorIsConstexpr();
|
||||
Trivial = RD->hasTrivialCopyConstructor();
|
||||
break;
|
||||
case CXXCopyAssignment:
|
||||
llvm::tie(Spec, Const) =
|
||||
llvm::tie(Spec, CanHaveConstParam) =
|
||||
ComputeDefaultedCopyAssignmentExceptionSpecAndConst(RD);
|
||||
Trivial = RD->hasTrivialCopyAssignment();
|
||||
break;
|
||||
case CXXMoveConstructor:
|
||||
Spec = ComputeDefaultedMoveCtorExceptionSpec(RD);
|
||||
Constexpr = RD->defaultedMoveConstructorIsConstexpr();
|
||||
Trivial = RD->hasTrivialMoveConstructor();
|
||||
break;
|
||||
case CXXMoveAssignment:
|
||||
|
@ -3970,9 +4069,11 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
|
||||
// Check for parameter type matching.
|
||||
QualType ArgType = ExpectedParams ? Type->getArgType(0) : QualType();
|
||||
bool HasConstParam = false;
|
||||
if (ExpectedParams && ArgType->isReferenceType()) {
|
||||
// Argument must be reference to possibly-const T.
|
||||
QualType ReferentType = ArgType->getPointeeType();
|
||||
HasConstParam = ReferentType.isConstQualified();
|
||||
|
||||
if (ReferentType.isVolatileQualified()) {
|
||||
Diag(MD->getLocation(),
|
||||
|
@ -3980,7 +4081,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
HadError = true;
|
||||
}
|
||||
|
||||
if (ReferentType.isConstQualified() && !Const) {
|
||||
if (HasConstParam && !CanHaveConstParam) {
|
||||
if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) {
|
||||
Diag(MD->getLocation(),
|
||||
diag::err_defaulted_special_member_copy_const_param)
|
||||
|
@ -3997,7 +4098,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
// If a function is explicitly defaulted on its first declaration, it shall
|
||||
// have the same parameter type as if it had been implicitly declared.
|
||||
// (Presumably this is to prevent it from being trivial?)
|
||||
if (!ReferentType.isConstQualified() && Const && First)
|
||||
if (!HasConstParam && CanHaveConstParam && First)
|
||||
Diag(MD->getLocation(),
|
||||
diag::err_defaulted_special_member_copy_non_const_param)
|
||||
<< (CSM == CXXCopyAssignment);
|
||||
|
@ -4021,9 +4122,12 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
// Do not apply this rule to members of class templates, since core issue 1358
|
||||
// makes such functions always instantiate to constexpr functions. For
|
||||
// non-constructors, this is checked elsewhere.
|
||||
bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
|
||||
HasConstParam);
|
||||
if (isa<CXXConstructorDecl>(MD) && MD->isConstexpr() && !Constexpr &&
|
||||
MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
|
||||
Diag(MD->getLocStart(), diag::err_incorrect_defaulted_constexpr) << CSM;
|
||||
// FIXME: Explain why the constructor can't be constexpr.
|
||||
HadError = true;
|
||||
}
|
||||
// and may have an explicit exception-specification only if it is compatible
|
||||
|
@ -6626,6 +6730,10 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
|||
ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
|
||||
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
|
||||
|
||||
bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
|
||||
CXXDefaultConstructor,
|
||||
false);
|
||||
|
||||
// Create the actual constructor declaration.
|
||||
CanQualType ClassType
|
||||
= Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
|
||||
|
@ -6637,8 +6745,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
|||
Context, ClassDecl, ClassLoc, NameInfo,
|
||||
Context.getFunctionType(Context.VoidTy, 0, 0, EPI), /*TInfo=*/0,
|
||||
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
|
||||
/*isConstexpr=*/ClassDecl->defaultedDefaultConstructorIsConstexpr() &&
|
||||
getLangOpts().CPlusPlus0x);
|
||||
Constexpr);
|
||||
DefaultCon->setAccess(AS_public);
|
||||
DefaultCon->setDefaulted();
|
||||
DefaultCon->setImplicit();
|
||||
|
@ -8474,6 +8581,10 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
|||
|
||||
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
|
||||
|
||||
bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
|
||||
CXXCopyConstructor,
|
||||
Const);
|
||||
|
||||
DeclarationName Name
|
||||
= Context.DeclarationNames.getCXXConstructorName(
|
||||
Context.getCanonicalType(ClassType));
|
||||
|
@ -8486,8 +8597,7 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
|||
Context, ClassDecl, ClassLoc, NameInfo,
|
||||
Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0,
|
||||
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
|
||||
/*isConstexpr=*/ClassDecl->defaultedCopyConstructorIsConstexpr() &&
|
||||
getLangOpts().CPlusPlus0x);
|
||||
Constexpr);
|
||||
CopyConstructor->setAccess(AS_public);
|
||||
CopyConstructor->setDefaulted();
|
||||
CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor());
|
||||
|
@ -8644,6 +8754,10 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
|
|||
|
||||
FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
|
||||
|
||||
bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
|
||||
CXXMoveConstructor,
|
||||
false);
|
||||
|
||||
DeclarationName Name
|
||||
= Context.DeclarationNames.getCXXConstructorName(
|
||||
Context.getCanonicalType(ClassType));
|
||||
|
@ -8657,8 +8771,7 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
|
|||
Context, ClassDecl, ClassLoc, NameInfo,
|
||||
Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI), /*TInfo=*/0,
|
||||
/*isExplicit=*/false, /*isInline=*/true, /*isImplicitlyDeclared=*/true,
|
||||
/*isConstexpr=*/ClassDecl->defaultedMoveConstructorIsConstexpr() &&
|
||||
getLangOpts().CPlusPlus0x);
|
||||
Constexpr);
|
||||
MoveConstructor->setAccess(AS_public);
|
||||
MoveConstructor->setDefaulted();
|
||||
MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor());
|
||||
|
|
|
@ -209,7 +209,7 @@ struct ConstexprBaseMemberCtors : Literal {
|
|||
{}
|
||||
};
|
||||
|
||||
// - every assignment-expression that is an initializer-caluse appearing
|
||||
// - every assignment-expression that is an initializer-clause appearing
|
||||
// directly or indirectly within a brace-or-equal-initializer for a non-static
|
||||
// data member that is not named by a mem-initializer-id shall be a constant
|
||||
// expression; and
|
||||
|
@ -224,6 +224,25 @@ struct X {
|
|||
constexpr X(int c) : a(c) {} // ok, b initialized by 2 * c + 1
|
||||
};
|
||||
|
||||
union XU1 { int a; constexpr XU1() = default; }; // expected-error{{not constexpr}}
|
||||
union XU2 { int a = 1; constexpr XU2() = default; };
|
||||
|
||||
struct XU3 {
|
||||
union {
|
||||
int a;
|
||||
};
|
||||
constexpr XU3() = default; // expected-error{{not constexpr}}
|
||||
};
|
||||
struct XU4 {
|
||||
union {
|
||||
int a = 1;
|
||||
};
|
||||
constexpr XU4() = default;
|
||||
};
|
||||
|
||||
static_assert(XU2().a == 1, "");
|
||||
static_assert(XU4().a == 1, "");
|
||||
|
||||
// - every implicit conversion used in converting a constructor argument to the
|
||||
// corresponding parameter type and converting a full-expression to the
|
||||
// corresponding member type shall be one of those allowed in a constant
|
||||
|
@ -247,3 +266,30 @@ namespace StdExample {
|
|||
int val;
|
||||
};
|
||||
}
|
||||
|
||||
namespace CtorLookup {
|
||||
// Ensure that we look up which constructor will actually be used.
|
||||
struct A {
|
||||
constexpr A(const A&) {}
|
||||
A(A&) {}
|
||||
constexpr A(int); // expected-note {{previous}}
|
||||
};
|
||||
constexpr A::A(int = 0) {} // expected-warning {{default constructor}}
|
||||
|
||||
struct B : A {
|
||||
B() = default;
|
||||
constexpr B(const B&);
|
||||
constexpr B(B&);
|
||||
};
|
||||
constexpr B::B(const B&) = default;
|
||||
constexpr B::B(B&) = default; // expected-error {{not constexpr}}
|
||||
|
||||
struct C {
|
||||
A a;
|
||||
C() = default;
|
||||
constexpr C(const C&);
|
||||
constexpr C(C&);
|
||||
};
|
||||
constexpr C::C(const C&) = default;
|
||||
constexpr C::C(C&) = default; // expected-error {{not constexpr}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue