forked from OSchip/llvm-project
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:
parent
c7c1ebe660
commit
41c35d6d2f
|
@ -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:
|
||||||
|
|
|
@ -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}}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
Loading…
Reference in New Issue