Unify lookup from within not-yet-defined defaulted special members: use common

code for handling triviality, deletedness and constexpr. Fix a few bugs in
these, particularly related to mutable members, and remove some dead code.

llvm-svn: 195809
This commit is contained in:
Richard Smith 2013-11-27 03:39:20 +00:00
parent c7c1ebe660
commit 41c35d6d2f
4 changed files with 115 additions and 49 deletions

View File

@ -4463,14 +4463,43 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
DeclareInheritingConstructors(Record); DeclareInheritingConstructors(Record);
} }
/// Look up the special member function that would be called by a special
/// member function for a subobject of class type.
///
/// \param Class The class type of the subobject.
/// \param CSM The kind of special member function.
/// \param FieldQuals If the subobject is a field, its cv-qualifiers.
/// \param ConstRHS True if this is a copy operation with a const object
/// on its RHS, that is, if the argument to the outer special member
/// function is 'const' and this is not a field marked 'mutable'.
static Sema::SpecialMemberOverloadResult *lookupCallFromSpecialMember(
Sema &S, CXXRecordDecl *Class, Sema::CXXSpecialMember CSM,
unsigned FieldQuals, bool ConstRHS) {
unsigned LHSQuals = 0;
if (CSM == Sema::CXXCopyAssignment || CSM == Sema::CXXMoveAssignment)
LHSQuals = FieldQuals;
unsigned RHSQuals = FieldQuals;
if (CSM == Sema::CXXDefaultConstructor || CSM == Sema::CXXDestructor)
RHSQuals = 0;
else if (ConstRHS)
RHSQuals |= Qualifiers::Const;
return S.LookupSpecialMember(Class, CSM,
RHSQuals & Qualifiers::Const,
RHSQuals & Qualifiers::Volatile,
false,
LHSQuals & Qualifiers::Const,
LHSQuals & Qualifiers::Volatile);
}
/// Is the special member function which would be selected to perform the /// Is the special member function which would be selected to perform the
/// specified operation on the specified class type a constexpr constructor? /// specified operation on the specified class type a constexpr constructor?
static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
Sema::CXXSpecialMember CSM, Sema::CXXSpecialMember CSM,
bool ConstArg) { unsigned Quals, bool ConstRHS) {
Sema::SpecialMemberOverloadResult *SMOR = Sema::SpecialMemberOverloadResult *SMOR =
S.LookupSpecialMember(ClassDecl, CSM, ConstArg, lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS);
false, false, false, false);
if (!SMOR || !SMOR->getMethod()) if (!SMOR || !SMOR->getMethod())
// A constructor we wouldn't select can't be "involved in initializing" // A constructor we wouldn't select can't be "involved in initializing"
// anything. // anything.
@ -4547,7 +4576,7 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
if (!BaseType) continue; if (!BaseType) continue;
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl()); CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, ConstArg)) if (!specialMemberIsConstexpr(S, BaseClassDecl, CSM, 0, ConstArg))
return false; return false;
} }
@ -4555,7 +4584,7 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
// [...] shall be a constexpr constructor; // [...] shall be a constexpr constructor;
// -- every non-static data member and base class sub-object shall be // -- every non-static data member and base class sub-object shall be
// initialized // initialized
// -- for each non-stastic data member of X that is of class type (or array // -- for each non-static data member of X that is of class type (or array
// thereof), the assignment operator selected to copy/move that member is // thereof), the assignment operator selected to copy/move that member is
// a constexpr function // a constexpr function
for (RecordDecl::field_iterator F = ClassDecl->field_begin(), for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
@ -4563,10 +4592,12 @@ static bool defaultedSpecialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
F != FEnd; ++F) { F != FEnd; ++F) {
if (F->isInvalidDecl()) if (F->isInvalidDecl())
continue; continue;
if (const RecordType *RecordTy = QualType BaseType = S.Context.getBaseElementType(F->getType());
S.Context.getBaseElementType(F->getType())->getAs<RecordType>()) { if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl()); CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM, ConstArg)) if (!specialMemberIsConstexpr(S, FieldRecDecl, CSM,
BaseType.getCVRQualifiers(),
ConstArg && !F->isMutable()))
return false; return false;
} }
} }
@ -4865,7 +4896,7 @@ struct SpecialMemberDeletionInfo {
bool Diagnose; bool Diagnose;
// Properties of the special member, computed for convenience. // Properties of the special member, computed for convenience.
bool IsConstructor, IsAssignment, IsMove, ConstArg, VolatileArg; bool IsConstructor, IsAssignment, IsMove, ConstArg;
SourceLocation Loc; SourceLocation Loc;
bool AllFieldsAreConst; bool AllFieldsAreConst;
@ -4874,7 +4905,7 @@ struct SpecialMemberDeletionInfo {
Sema::CXXSpecialMember CSM, bool Diagnose) Sema::CXXSpecialMember CSM, bool Diagnose)
: S(S), MD(MD), CSM(CSM), Diagnose(Diagnose), : S(S), MD(MD), CSM(CSM), Diagnose(Diagnose),
IsConstructor(false), IsAssignment(false), IsMove(false), IsConstructor(false), IsAssignment(false), IsMove(false),
ConstArg(false), VolatileArg(false), Loc(MD->getLocation()), ConstArg(false), Loc(MD->getLocation()),
AllFieldsAreConst(true) { AllFieldsAreConst(true) {
switch (CSM) { switch (CSM) {
case Sema::CXXDefaultConstructor: case Sema::CXXDefaultConstructor:
@ -4899,8 +4930,9 @@ struct SpecialMemberDeletionInfo {
} }
if (MD->getNumParams()) { if (MD->getNumParams()) {
ConstArg = MD->getParamDecl(0)->getType().isConstQualified(); if (const ReferenceType *RT =
VolatileArg = MD->getParamDecl(0)->getType().isVolatileQualified(); MD->getParamDecl(0)->getType()->getAs<ReferenceType>())
ConstArg = RT->getPointeeType().isConstQualified();
} }
} }
@ -4908,21 +4940,9 @@ struct SpecialMemberDeletionInfo {
/// Look up the corresponding special member in the given class. /// Look up the corresponding special member in the given class.
Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class, Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class,
unsigned Quals) { unsigned Quals, bool IsMutable) {
unsigned TQ = MD->getTypeQualifiers(); return lookupCallFromSpecialMember(S, Class, CSM, Quals,
// cv-qualifiers on class members don't affect default ctor / dtor calls. ConstArg && !IsMutable);
if (CSM == Sema::CXXDefaultConstructor || CSM == Sema::CXXDestructor)
Quals = 0;
// cv-qualifiers on class members affect the type of both '*this' and the
// argument for an assignment.
if (IsAssignment)
TQ |= Quals;
return S.LookupSpecialMember(Class, CSM,
ConstArg || (Quals & Qualifiers::Const),
VolatileArg || (Quals & Qualifiers::Volatile),
MD->getRefQualifier() == RQ_RValue,
TQ & Qualifiers::Const,
TQ & Qualifiers::Volatile);
} }
typedef llvm::PointerUnion<CXXBaseSpecifier*, FieldDecl*> Subobject; typedef llvm::PointerUnion<CXXBaseSpecifier*, FieldDecl*> Subobject;
@ -5017,6 +5037,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject( bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
CXXRecordDecl *Class, Subobject Subobj, unsigned Quals) { CXXRecordDecl *Class, Subobject Subobj, unsigned Quals) {
FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>(); FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>();
bool IsMutable = Field && Field->isMutable();
// C++11 [class.ctor]p5: // C++11 [class.ctor]p5:
// -- any direct or virtual base class, or non-static data member with no // -- any direct or virtual base class, or non-static data member with no
@ -5034,7 +5055,8 @@ bool SpecialMemberDeletionInfo::shouldDeleteForClassSubobject(
// that is deleted or inaccessible // that is deleted or inaccessible
if (!(CSM == Sema::CXXDefaultConstructor && if (!(CSM == Sema::CXXDefaultConstructor &&
Field && Field->hasInClassInitializer()) && Field && Field->hasInClassInitializer()) &&
shouldDeleteForSubobjectCall(Subobj, lookupIn(Class, Quals), false)) shouldDeleteForSubobjectCall(Subobj, lookupIn(Class, Quals, IsMutable),
false))
return true; return true;
// C++11 [class.ctor]p5, C++11 [class.copy]p11: // C++11 [class.ctor]p5, C++11 [class.copy]p11:
@ -5312,7 +5334,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
/// member that was most likely to be intended to be trivial, if any. /// member that was most likely to be intended to be trivial, if any.
static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD, static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
Sema::CXXSpecialMember CSM, unsigned Quals, Sema::CXXSpecialMember CSM, unsigned Quals,
CXXMethodDecl **Selected) { bool ConstRHS, CXXMethodDecl **Selected) {
if (Selected) if (Selected)
*Selected = 0; *Selected = 0;
@ -5403,11 +5425,7 @@ static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD,
case Sema::CXXMoveAssignment: case Sema::CXXMoveAssignment:
NeedOverloadResolution: NeedOverloadResolution:
Sema::SpecialMemberOverloadResult *SMOR = Sema::SpecialMemberOverloadResult *SMOR =
S.LookupSpecialMember(RD, CSM, lookupCallFromSpecialMember(S, RD, CSM, Quals, ConstRHS);
Quals & Qualifiers::Const,
Quals & Qualifiers::Volatile,
/*RValueThis*/false, /*ConstThis*/false,
/*VolatileThis*/false);
// The standard doesn't describe how to behave if the lookup is ambiguous. // The standard doesn't describe how to behave if the lookup is ambiguous.
// We treat it as not making the member non-trivial, just like the standard // We treat it as not making the member non-trivial, just like the standard
@ -5462,7 +5480,7 @@ enum TrivialSubobjectKind {
/// Check whether the special member selected for a given type would be trivial. /// Check whether the special member selected for a given type would be trivial.
static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc, static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc,
QualType SubType, QualType SubType, bool ConstRHS,
Sema::CXXSpecialMember CSM, Sema::CXXSpecialMember CSM,
TrivialSubobjectKind Kind, TrivialSubobjectKind Kind,
bool Diagnose) { bool Diagnose) {
@ -5472,10 +5490,13 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc,
CXXMethodDecl *Selected; CXXMethodDecl *Selected;
if (findTrivialSpecialMember(S, SubRD, CSM, SubType.getCVRQualifiers(), if (findTrivialSpecialMember(S, SubRD, CSM, SubType.getCVRQualifiers(),
Diagnose ? &Selected : 0)) ConstRHS, Diagnose ? &Selected : 0))
return true; return true;
if (Diagnose) { if (Diagnose) {
if (ConstRHS)
SubType.addConst();
if (!Selected && CSM == Sema::CXXDefaultConstructor) { if (!Selected && CSM == Sema::CXXDefaultConstructor) {
S.Diag(SubobjLoc, diag::note_nontrivial_no_def_ctor) S.Diag(SubobjLoc, diag::note_nontrivial_no_def_ctor)
<< Kind << SubType.getUnqualifiedType(); << Kind << SubType.getUnqualifiedType();
@ -5548,10 +5569,9 @@ static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD,
return false; return false;
} }
if (ConstArg && !FI->isMutable()) bool ConstRHS = ConstArg && !FI->isMutable();
FieldType.addConst(); if (!checkTrivialSubobjectCall(S, FI->getLocation(), FieldType, ConstRHS,
if (!checkTrivialSubobjectCall(S, FI->getLocation(), FieldType, CSM, CSM, TSK_Field, Diagnose))
TSK_Field, Diagnose))
return false; return false;
} }
@ -5562,10 +5582,9 @@ static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD,
/// the given kind. /// the given kind.
void Sema::DiagnoseNontrivial(const CXXRecordDecl *RD, CXXSpecialMember CSM) { void Sema::DiagnoseNontrivial(const CXXRecordDecl *RD, CXXSpecialMember CSM) {
QualType Ty = Context.getRecordType(RD); QualType Ty = Context.getRecordType(RD);
if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment)
Ty.addConst();
checkTrivialSubobjectCall(*this, RD->getLocation(), Ty, CSM, bool ConstArg = (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment);
checkTrivialSubobjectCall(*this, RD->getLocation(), Ty, ConstArg, CSM,
TSK_CompleteObject, /*Diagnose*/true); TSK_CompleteObject, /*Diagnose*/true);
} }
@ -5650,10 +5669,8 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
// destructors] // destructors]
for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
BE = RD->bases_end(); BI != BE; ++BI) BE = RD->bases_end(); BI != BE; ++BI)
if (!checkTrivialSubobjectCall(*this, BI->getLocStart(), if (!checkTrivialSubobjectCall(*this, BI->getLocStart(), BI->getType(),
ConstArg ? BI->getType().withConst() ConstArg, CSM, TSK_BaseClass, Diagnose))
: BI->getType(),
CSM, TSK_BaseClass, Diagnose))
return false; return false;
// C++11 [class.ctor]p5, C++11 [class.dtor]p5: // C++11 [class.ctor]p5, C++11 [class.dtor]p5:

View File

@ -139,3 +139,22 @@ namespace PR13381 {
T &f(); T &f();
T t = f(); // expected-error{{call to implicitly-deleted copy constructor}} T t = f(); // expected-error{{call to implicitly-deleted copy constructor}}
} }
namespace Mutable {
struct A {
A(const A &);
A(A &) = delete;
};
struct B {
A a;
B(const B &);
};
B::B(const B &) = default;
struct C {
mutable A a;
C(const C &);
};
C::C(const C &) = default; // expected-error{{would delete}}
}

View File

@ -114,3 +114,18 @@ namespace PR13052 {
friend constexpr S<W>::S(const S<W>&) noexcept; friend constexpr S<W>::S(const S<W>&) noexcept;
}; };
} }
namespace Mutable {
struct A {
constexpr A(A &);
A(const A &);
};
struct B {
constexpr B(const B &) = default; // ok
mutable A a;
};
struct C {
constexpr C(const C &) = default; // expected-error {{not constexpr}}
A a;
};
}

View File

@ -30,8 +30,8 @@ struct NonTrivialMoveAssign {
NonTrivialMoveAssign &operator=(NonTrivialMoveAssign &&); NonTrivialMoveAssign &operator=(NonTrivialMoveAssign &&);
}; };
struct AmbiguousCopyAssign { struct AmbiguousCopyAssign {
AmbiguousCopyAssign &operator=(const AmbiguousCopyAssign &); AmbiguousCopyAssign &operator=(const AmbiguousCopyAssign &) volatile;
AmbiguousCopyAssign &operator=(volatile AmbiguousCopyAssign &); AmbiguousCopyAssign &operator=(const AmbiguousCopyAssign &) const;
}; };
struct AmbiguousMoveAssign { struct AmbiguousMoveAssign {
AmbiguousMoveAssign &operator=(const AmbiguousMoveAssign &&); AmbiguousMoveAssign &operator=(const AmbiguousMoveAssign &&);
@ -174,3 +174,18 @@ namespace PR13381 {
t = T(); // expected-error{{object of type 'PR13381::T' cannot be assigned because its copy assignment operator is implicitly deleted}} t = T(); // expected-error{{object of type 'PR13381::T' cannot be assigned because its copy assignment operator is implicitly deleted}}
} }
} }
namespace Mutable {
struct AmbiguousCopyAssign {
AmbiguousCopyAssign &operator=(const AmbiguousCopyAssign &);
AmbiguousCopyAssign &operator=(volatile AmbiguousCopyAssign &);
};
struct X {
AmbiguousCopyAssign a;
};
struct Y {
mutable AmbiguousCopyAssign a; // expected-note {{multiple copy assignment operators}}
};
}
template struct CopyAssign<Mutable::X>;
template struct CopyAssign<Mutable::Y>; // expected-note {{here}}