forked from OSchip/llvm-project
Store on the CXXRecordDecl whether the class has, or would have, a copy
constructor/assignment operator with a const-qualified parameter type. The prior method for determining this incorrectly used overload resolution. llvm-svn: 168775
This commit is contained in:
parent
94c8348859
commit
1c33fe8fea
|
@ -494,6 +494,22 @@ class CXXRecordDecl : public RecordDecl {
|
|||
/// \brief Whether we have already declared a destructor within the class.
|
||||
bool DeclaredDestructor : 1;
|
||||
|
||||
/// \brief Whether an implicit copy constructor would have a const-qualified
|
||||
/// parameter.
|
||||
bool ImplicitCopyConstructorHasConstParam : 1;
|
||||
|
||||
/// \brief Whether an implicit copy assignment operator would have a
|
||||
/// const-qualified parameter.
|
||||
bool ImplicitCopyAssignmentHasConstParam : 1;
|
||||
|
||||
/// \brief Whether any declared copy constructor has a const-qualified
|
||||
/// parameter.
|
||||
bool HasDeclaredCopyConstructorWithConstParam : 1;
|
||||
|
||||
/// \brief Whether any declared copy assignment operator has either a
|
||||
/// const-qualified reference parameter or a non-reference parameter.
|
||||
bool HasDeclaredCopyAssignmentWithConstParam : 1;
|
||||
|
||||
/// \brief Whether an implicit move constructor was attempted to be declared
|
||||
/// but would have been deleted.
|
||||
bool FailedImplicitMoveConstructor : 1;
|
||||
|
@ -881,6 +897,20 @@ public:
|
|||
return data().DeclaredCopyConstructor;
|
||||
}
|
||||
|
||||
/// \brief Determine whether an implicit copy constructor for this type
|
||||
/// would have a parameter with a const-qualified reference type.
|
||||
bool implicitCopyConstructorHasConstParam() const {
|
||||
return data().ImplicitCopyConstructorHasConstParam;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a copy constructor with
|
||||
/// a parameter type which is a reference to a const-qualified type.
|
||||
bool hasCopyConstructorWithConstParam() const {
|
||||
return data().HasDeclaredCopyConstructorWithConstParam ||
|
||||
(!hasDeclaredCopyConstructor() &&
|
||||
implicitCopyConstructorHasConstParam());
|
||||
}
|
||||
|
||||
/// hasUserDeclaredMoveOperation - Whether this class has a user-
|
||||
/// declared move constructor or assignment operator. When false, a
|
||||
/// move constructor and assignment operator may be implicitly declared.
|
||||
|
@ -943,6 +973,21 @@ public:
|
|||
return data().DeclaredCopyAssignment;
|
||||
}
|
||||
|
||||
/// \brief Determine whether an implicit copy assignment operator for this
|
||||
/// type would have a parameter with a const-qualified reference type.
|
||||
bool implicitCopyAssignmentHasConstParam() const {
|
||||
return data().ImplicitCopyAssignmentHasConstParam;
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has a copy assignment operator with
|
||||
/// a parameter type which is a reference to a const-qualified type or is not
|
||||
/// a reference..
|
||||
bool hasCopyAssignmentWithConstParam() const {
|
||||
return data().HasDeclaredCopyAssignmentWithConstParam ||
|
||||
(!hasDeclaredCopyAssignment() &&
|
||||
implicitCopyAssignmentHasConstParam());
|
||||
}
|
||||
|
||||
/// \brief Determine whether this class has had a move assignment
|
||||
/// declared by the user.
|
||||
bool hasUserDeclaredMoveAssignment() const {
|
||||
|
@ -2126,7 +2171,7 @@ public:
|
|||
/// constructor (C++ [class.copy]p2, which can be used to copy the
|
||||
/// class. @p TypeQuals will be set to the qualifiers on the
|
||||
/// argument type. For example, @p TypeQuals would be set to @c
|
||||
/// QualType::Const for the following copy constructor:
|
||||
/// Qualifiers::Const for the following copy constructor:
|
||||
///
|
||||
/// @code
|
||||
/// class X {
|
||||
|
|
|
@ -1950,6 +1950,14 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
|
|||
ToData.DeclaredCopyAssignment = FromData.DeclaredCopyAssignment;
|
||||
ToData.DeclaredMoveAssignment = FromData.DeclaredMoveAssignment;
|
||||
ToData.DeclaredDestructor = FromData.DeclaredDestructor;
|
||||
ToData.ImplicitCopyConstructorHasConstParam
|
||||
= FromData.ImplicitCopyConstructorHasConstParam;
|
||||
ToData.ImplicitCopyAssignmentHasConstParam
|
||||
= FromData.ImplicitCopyAssignmentHasConstParam;
|
||||
ToData.HasDeclaredCopyConstructorWithConstParam
|
||||
= FromData.HasDeclaredCopyConstructorWithConstParam;
|
||||
ToData.HasDeclaredCopyAssignmentWithConstParam
|
||||
= FromData.HasDeclaredCopyAssignmentWithConstParam;
|
||||
ToData.FailedImplicitMoveConstructor
|
||||
= FromData.FailedImplicitMoveConstructor;
|
||||
ToData.FailedImplicitMoveAssignment = FromData.FailedImplicitMoveAssignment;
|
||||
|
|
|
@ -55,9 +55,14 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
|
|||
UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false),
|
||||
DeclaredCopyConstructor(false), DeclaredMoveConstructor(false),
|
||||
DeclaredCopyAssignment(false), DeclaredMoveAssignment(false),
|
||||
DeclaredDestructor(false), FailedImplicitMoveConstructor(false),
|
||||
FailedImplicitMoveAssignment(false), IsLambda(false), NumBases(0),
|
||||
NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) {
|
||||
DeclaredDestructor(false),
|
||||
ImplicitCopyConstructorHasConstParam(true),
|
||||
ImplicitCopyAssignmentHasConstParam(true),
|
||||
HasDeclaredCopyConstructorWithConstParam(false),
|
||||
HasDeclaredCopyAssignmentWithConstParam(false),
|
||||
FailedImplicitMoveConstructor(false), FailedImplicitMoveAssignment(false),
|
||||
IsLambda(false), NumBases(0), NumVBases(0), Bases(), VBases(),
|
||||
Definition(D), FirstFriend(0) {
|
||||
}
|
||||
|
||||
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
|
||||
|
@ -184,15 +189,25 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
|||
BaseClassDecl->vbases_begin(),
|
||||
E = BaseClassDecl->vbases_end(); VBase != E; ++VBase) {
|
||||
// Add this base if it's not already in the list.
|
||||
if (SeenVBaseTypes.insert(C.getCanonicalType(VBase->getType())))
|
||||
if (SeenVBaseTypes.insert(C.getCanonicalType(VBase->getType()))) {
|
||||
VBases.push_back(VBase);
|
||||
|
||||
// C++11 [class.copy]p8:
|
||||
// The implicitly-declared copy constructor for a class X will have
|
||||
// the form 'X::X(const X&)' if each [...] virtual base class B of X
|
||||
// has a copy constructor whose first parameter is of type
|
||||
// 'const B&' or 'const volatile B&' [...]
|
||||
if (CXXRecordDecl *VBaseDecl = VBase->getType()->getAsCXXRecordDecl())
|
||||
if (!VBaseDecl->hasCopyConstructorWithConstParam())
|
||||
data().ImplicitCopyConstructorHasConstParam = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Base->isVirtual()) {
|
||||
// Add this base if it's not already in the list.
|
||||
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)))
|
||||
VBases.push_back(Base);
|
||||
|
||||
VBases.push_back(Base);
|
||||
|
||||
// C++0x [meta.unary.prop] is_empty:
|
||||
// T is a class type, but not a union type, with ... no virtual base
|
||||
// classes
|
||||
|
@ -276,6 +291,22 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
|||
if (!BaseClassDecl->hasIrrelevantDestructor())
|
||||
data().HasIrrelevantDestructor = false;
|
||||
|
||||
// C++11 [class.copy]p18:
|
||||
// The implicitly-declared copy assignment oeprator for a class X will
|
||||
// have the form 'X& X::operator=(const X&)' if each direct base class B
|
||||
// of X has a copy assignment operator whose parameter is of type 'const
|
||||
// B&', 'const volatile B&', or 'B' [...]
|
||||
if (!BaseClassDecl->hasCopyAssignmentWithConstParam())
|
||||
data().ImplicitCopyAssignmentHasConstParam = false;
|
||||
|
||||
// C++11 [class.copy]p8:
|
||||
// The implicitly-declared copy constructor for a class X will have
|
||||
// the form 'X::X(const X&)' if each direct [...] base class B of X
|
||||
// has a copy constructor whose first parameter is of type
|
||||
// 'const B&' or 'const volatile B&' [...]
|
||||
if (!BaseClassDecl->hasCopyConstructorWithConstParam())
|
||||
data().ImplicitCopyConstructorHasConstParam = false;
|
||||
|
||||
// A class has an Objective-C object member if... or any of its bases
|
||||
// has an Objective-C object member.
|
||||
if (BaseClassDecl->hasObjectMember())
|
||||
|
@ -522,51 +553,25 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
data().IsStandardLayout = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (D->isImplicit()) {
|
||||
// Notify that an implicit member was added after the definition
|
||||
// was completed.
|
||||
if (!isBeingDefined())
|
||||
if (ASTMutationListener *L = getASTMutationListener())
|
||||
L->AddedCXXImplicitMember(data().Definition, D);
|
||||
|
||||
// If this is a special member function, note that it was added and then
|
||||
// return early.
|
||||
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
if (Constructor->isDefaultConstructor()) {
|
||||
data().DeclaredDefaultConstructor = true;
|
||||
if (Constructor->isConstexpr()) {
|
||||
data().HasConstexprDefaultConstructor = true;
|
||||
data().HasConstexprNonCopyMoveConstructor = true;
|
||||
}
|
||||
} else if (Constructor->isCopyConstructor()) {
|
||||
data().DeclaredCopyConstructor = true;
|
||||
} else if (Constructor->isMoveConstructor()) {
|
||||
data().DeclaredMoveConstructor = true;
|
||||
} else
|
||||
goto NotASpecialMember;
|
||||
return;
|
||||
} else if (isa<CXXDestructorDecl>(D)) {
|
||||
data().DeclaredDestructor = true;
|
||||
return;
|
||||
} else if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
if (Method->isCopyAssignmentOperator())
|
||||
data().DeclaredCopyAssignment = true;
|
||||
else if (Method->isMoveAssignmentOperator())
|
||||
data().DeclaredMoveAssignment = true;
|
||||
else
|
||||
goto NotASpecialMember;
|
||||
return;
|
||||
}
|
||||
// Notify the listener if an implicit member was added after the definition
|
||||
// was completed.
|
||||
if (!isBeingDefined() && D->isImplicit())
|
||||
if (ASTMutationListener *L = getASTMutationListener())
|
||||
L->AddedCXXImplicitMember(data().Definition, D);
|
||||
|
||||
NotASpecialMember:;
|
||||
// Any other implicit declarations are handled like normal declarations.
|
||||
}
|
||||
|
||||
// Handle (user-declared) constructors.
|
||||
// Handle constructors.
|
||||
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
// Note that we have a user-declared constructor.
|
||||
data().UserDeclaredConstructor = true;
|
||||
if (!Constructor->isImplicit()) {
|
||||
// Note that we have a user-declared constructor.
|
||||
data().UserDeclaredConstructor = true;
|
||||
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class [...]
|
||||
// Since the POD bit is meant to be C++03 POD-ness, clear it even if the
|
||||
// type is technically an aggregate in C++0x since it wouldn't be in 03.
|
||||
data().PlainOldData = false;
|
||||
}
|
||||
|
||||
// Technically, "user-provided" is only defined for special member
|
||||
// functions, but the intent of the standard is clearly that it should apply
|
||||
|
@ -581,17 +586,17 @@ NotASpecialMember:;
|
|||
data().HasTrivialDefaultConstructor = false;
|
||||
data().UserProvidedDefaultConstructor = true;
|
||||
}
|
||||
if (Constructor->isConstexpr()) {
|
||||
if (Constructor->isConstexpr())
|
||||
data().HasConstexprDefaultConstructor = true;
|
||||
data().HasConstexprNonCopyMoveConstructor = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Note when we have a user-declared copy or move constructor, which will
|
||||
// suppress the implicit declaration of those constructors.
|
||||
if (!FunTmpl) {
|
||||
if (Constructor->isCopyConstructor()) {
|
||||
data().UserDeclaredCopyConstructor = true;
|
||||
unsigned Quals;
|
||||
if (Constructor->isCopyConstructor(Quals)) {
|
||||
if (!Constructor->isImplicit())
|
||||
data().UserDeclaredCopyConstructor = true;
|
||||
data().DeclaredCopyConstructor = true;
|
||||
|
||||
// C++0x [class.copy]p13:
|
||||
|
@ -599,8 +604,12 @@ NotASpecialMember:;
|
|||
// user-provided [...]
|
||||
if (UserProvided)
|
||||
data().HasTrivialCopyConstructor = false;
|
||||
|
||||
if (Quals & Qualifiers::Const)
|
||||
data().HasDeclaredCopyConstructorWithConstParam = true;
|
||||
} else if (Constructor->isMoveConstructor()) {
|
||||
data().UserDeclaredMoveConstructor = true;
|
||||
if (!Constructor->isImplicit())
|
||||
data().UserDeclaredMoveConstructor = true;
|
||||
data().DeclaredMoveConstructor = true;
|
||||
|
||||
// C++0x [class.copy]p13:
|
||||
|
@ -610,11 +619,11 @@ NotASpecialMember:;
|
|||
data().HasTrivialMoveConstructor = false;
|
||||
}
|
||||
}
|
||||
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) {
|
||||
// Record if we see any constexpr constructors which are neither copy
|
||||
// nor move constructors.
|
||||
|
||||
// Record if we see any constexpr constructors which are neither copy
|
||||
// nor move constructors.
|
||||
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor())
|
||||
data().HasConstexprNonCopyMoveConstructor = true;
|
||||
}
|
||||
|
||||
// C++ [dcl.init.aggr]p1:
|
||||
// An aggregate is an array or a class with no user-declared
|
||||
|
@ -622,31 +631,29 @@ NotASpecialMember:;
|
|||
// C++0x [dcl.init.aggr]p1:
|
||||
// An aggregate is an array or a class with no user-provided
|
||||
// constructors [...].
|
||||
if (!getASTContext().getLangOpts().CPlusPlus0x || UserProvided)
|
||||
if (getASTContext().getLangOpts().CPlusPlus0x
|
||||
? UserProvided : !Constructor->isImplicit())
|
||||
data().Aggregate = false;
|
||||
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class [...]
|
||||
// Since the POD bit is meant to be C++03 POD-ness, clear it even if the
|
||||
// type is technically an aggregate in C++0x since it wouldn't be in 03.
|
||||
data().PlainOldData = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle (user-declared) destructors.
|
||||
// Handle destructors.
|
||||
if (CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D)) {
|
||||
data().DeclaredDestructor = true;
|
||||
data().UserDeclaredDestructor = true;
|
||||
data().HasIrrelevantDestructor = false;
|
||||
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class that has [...] no user-defined
|
||||
// destructor.
|
||||
// This bit is the C++03 POD bit, not the 0x one.
|
||||
data().PlainOldData = false;
|
||||
|
||||
// C++11 [class.dtor]p5:
|
||||
if (!DD->isImplicit()) {
|
||||
data().UserDeclaredDestructor = true;
|
||||
data().HasIrrelevantDestructor = false;
|
||||
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class that has [...] no user-defined
|
||||
// destructor.
|
||||
// This bit is the C++03 POD bit, not the 0x one.
|
||||
data().PlainOldData = false;
|
||||
}
|
||||
|
||||
// C++11 [class.dtor]p5:
|
||||
// A destructor is trivial if it is not user-provided and if
|
||||
// -- the destructor is not virtual.
|
||||
if (DD->isUserProvided() || DD->isVirtual())
|
||||
|
@ -654,45 +661,53 @@ NotASpecialMember:;
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle (user-declared) member functions.
|
||||
|
||||
// Handle member functions.
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
if (Method->isCopyAssignmentOperator()) {
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class that [...] has no user-defined
|
||||
// copy assignment operator [...].
|
||||
// This is the C++03 bit only.
|
||||
data().PlainOldData = false;
|
||||
|
||||
// This is a copy assignment operator.
|
||||
|
||||
// Suppress the implicit declaration of a copy constructor.
|
||||
data().UserDeclaredCopyAssignment = true;
|
||||
data().DeclaredCopyAssignment = true;
|
||||
|
||||
// C++0x [class.copy]p27:
|
||||
// A copy/move assignment operator for class X is trivial if it is
|
||||
// neither user-provided nor deleted [...]
|
||||
if (Method->isUserProvided())
|
||||
data().HasTrivialCopyAssignment = false;
|
||||
if (!Method->isImplicit()) {
|
||||
data().UserDeclaredCopyAssignment = true;
|
||||
|
||||
return;
|
||||
// C++ [class]p4:
|
||||
// A POD-struct is an aggregate class that [...] has no user-defined
|
||||
// copy assignment operator [...].
|
||||
// This is the C++03 bit only.
|
||||
data().PlainOldData = false;
|
||||
|
||||
// C++11 [class.copy]p25:
|
||||
// A copy/move assignment operator for class X is trivial if it is
|
||||
// not user-provided [...]
|
||||
// FIXME: This is bogus. Having one user-provided copy assignment
|
||||
// doesn't stop another one from being trivial.
|
||||
if (Method->isUserProvided())
|
||||
data().HasTrivialCopyAssignment = false;
|
||||
}
|
||||
|
||||
const ReferenceType *ParamTy =
|
||||
Method->getParamDecl(0)->getType()->getAs<ReferenceType>();
|
||||
if (!ParamTy || ParamTy->getPointeeType().isConstQualified())
|
||||
data().HasDeclaredCopyAssignmentWithConstParam = true;
|
||||
}
|
||||
|
||||
if (Method->isMoveAssignmentOperator()) {
|
||||
// This is an extension in C++03 mode, but we'll keep consistency by
|
||||
// taking a move assignment operator to induce non-POD-ness
|
||||
data().PlainOldData = false;
|
||||
|
||||
// This is a move assignment operator.
|
||||
data().UserDeclaredMoveAssignment = true;
|
||||
if (Method->isMoveAssignmentOperator()) {
|
||||
data().DeclaredMoveAssignment = true;
|
||||
|
||||
// C++0x [class.copy]p27:
|
||||
// A copy/move assignment operator for class X is trivial if it is
|
||||
// neither user-provided nor deleted [...]
|
||||
if (Method->isUserProvided())
|
||||
data().HasTrivialMoveAssignment = false;
|
||||
if (!Method->isImplicit()) {
|
||||
data().UserDeclaredMoveAssignment = true;
|
||||
|
||||
// This is an extension in C++03 mode, but we'll keep consistency by
|
||||
// taking a move assignment operator to induce non-POD-ness
|
||||
data().PlainOldData = false;
|
||||
|
||||
// C++0x [class.copy]p27:
|
||||
// A copy/move assignment operator for class X is trivial if it is
|
||||
// neither user-provided nor deleted [...]
|
||||
if (Method->isUserProvided())
|
||||
data().HasTrivialMoveAssignment = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the list of conversion functions up-to-date.
|
||||
|
@ -700,7 +715,7 @@ NotASpecialMember:;
|
|||
// We don't record specializations.
|
||||
if (Conversion->getPrimaryTemplate())
|
||||
return;
|
||||
|
||||
|
||||
// FIXME: We intentionally don't use the decl's access here because it
|
||||
// hasn't been set yet. That's really just a misdesign in Sema.
|
||||
|
||||
|
@ -718,7 +733,7 @@ NotASpecialMember:;
|
|||
data().Conversions.addDecl(getASTContext(), Conversion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -902,6 +917,24 @@ NotASpecialMember:;
|
|||
// The standard requires any in-class initializer to be a constant
|
||||
// expression. We consider this to be a defect.
|
||||
data().DefaultedDefaultConstructorIsConstexpr = false;
|
||||
|
||||
// C++11 [class.copy]p8:
|
||||
// The implicitly-declared copy constructor for a class X will have
|
||||
// the form 'X::X(const X&)' if [...] for all the non-static data
|
||||
// members of X that are of a class type M (or array thereof), each
|
||||
// such class type has a copy constructor whose first parameter is
|
||||
// of type 'const M&' or 'const volatile M&'.
|
||||
if (!FieldRec->hasCopyConstructorWithConstParam())
|
||||
data().ImplicitCopyConstructorHasConstParam = false;
|
||||
|
||||
// C++11 [class.copy]p18:
|
||||
// The implicitly-declared copy assignment oeprator for a class X will
|
||||
// have the form 'X& X::operator=(const X&)' if [...] for all the
|
||||
// non-static data members of X that are of a class type M (or array
|
||||
// thereof), each such class type has a copy assignment operator whose
|
||||
// parameter is of type 'const M&', 'const volatile M&' or 'M'.
|
||||
if (!FieldRec->hasCopyAssignmentWithConstParam())
|
||||
data().ImplicitCopyAssignmentHasConstParam = false;
|
||||
}
|
||||
} else {
|
||||
// Base element type of field is a non-class type.
|
||||
|
|
|
@ -4193,9 +4193,6 @@ void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD)
|
|||
CanonicalFPT, ExceptSpec);
|
||||
}
|
||||
|
||||
static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl);
|
||||
static bool isImplicitCopyAssignmentArgConst(Sema &S, CXXRecordDecl *ClassDecl);
|
||||
|
||||
void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
||||
CXXRecordDecl *RD = MD->getParent();
|
||||
CXXSpecialMember CSM = getSpecialMember(MD);
|
||||
|
@ -4238,11 +4235,11 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
|
|||
Trivial = RD->hasTrivialDefaultConstructor();
|
||||
break;
|
||||
case CXXCopyConstructor:
|
||||
CanHaveConstParam = isImplicitCopyCtorArgConst(*this, RD);
|
||||
CanHaveConstParam = RD->implicitCopyConstructorHasConstParam();
|
||||
Trivial = RD->hasTrivialCopyConstructor();
|
||||
break;
|
||||
case CXXCopyAssignment:
|
||||
CanHaveConstParam = isImplicitCopyAssignmentArgConst(*this, RD);
|
||||
CanHaveConstParam = RD->implicitCopyAssignmentHasConstParam();
|
||||
Trivial = RD->hasTrivialCopyAssignment();
|
||||
break;
|
||||
case CXXMoveConstructor:
|
||||
|
@ -7727,76 +7724,6 @@ buildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T,
|
|||
return Result;
|
||||
}
|
||||
|
||||
/// Determine whether an implicit copy assignment operator for ClassDecl has a
|
||||
/// const argument.
|
||||
/// FIXME: It ought to be possible to store this on the record.
|
||||
static bool isImplicitCopyAssignmentArgConst(Sema &S,
|
||||
CXXRecordDecl *ClassDecl) {
|
||||
if (ClassDecl->isInvalidDecl())
|
||||
return true;
|
||||
|
||||
// C++ [class.copy]p10:
|
||||
// If the class definition does not explicitly declare a copy
|
||||
// assignment operator, one is declared implicitly.
|
||||
// The implicitly-defined copy assignment operator for a class X
|
||||
// will have the form
|
||||
//
|
||||
// X& X::operator=(const X&)
|
||||
//
|
||||
// if
|
||||
// -- each direct base class B of X has a copy assignment operator
|
||||
// whose parameter is of type const B&, const volatile B& or B,
|
||||
// and
|
||||
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
|
||||
BaseEnd = ClassDecl->bases_end();
|
||||
Base != BaseEnd; ++Base) {
|
||||
// We'll handle this below
|
||||
if (S.getLangOpts().CPlusPlus0x && Base->isVirtual())
|
||||
continue;
|
||||
|
||||
assert(!Base->getType()->isDependentType() &&
|
||||
"Cannot generate implicit members for class with dependent bases.");
|
||||
CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl();
|
||||
if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
// In C++11, the above citation has "or virtual" added
|
||||
if (S.getLangOpts().CPlusPlus0x) {
|
||||
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
|
||||
BaseEnd = ClassDecl->vbases_end();
|
||||
Base != BaseEnd; ++Base) {
|
||||
assert(!Base->getType()->isDependentType() &&
|
||||
"Cannot generate implicit members for class with dependent bases.");
|
||||
CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl();
|
||||
if (!S.LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const,
|
||||
false, 0))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// -- for all the nonstatic data members of X that are of a class
|
||||
// type M (or array thereof), each such class type has a copy
|
||||
// assignment operator whose parameter is of type const M&,
|
||||
// const volatile M& or M.
|
||||
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
|
||||
FieldEnd = ClassDecl->field_end();
|
||||
Field != FieldEnd; ++Field) {
|
||||
QualType FieldType = S.Context.getBaseElementType(Field->getType());
|
||||
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl())
|
||||
if (!S.LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const,
|
||||
false, 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, the implicitly declared copy assignment operator will
|
||||
// have the form
|
||||
//
|
||||
// X& X::operator=(X&)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Sema::ImplicitExceptionSpecification
|
||||
Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) {
|
||||
CXXRecordDecl *ClassDecl = MD->getParent();
|
||||
|
@ -7868,7 +7795,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
|
|||
|
||||
QualType ArgType = Context.getTypeDeclType(ClassDecl);
|
||||
QualType RetType = Context.getLValueReferenceType(ArgType);
|
||||
if (isImplicitCopyAssignmentArgConst(*this, ClassDecl))
|
||||
if (ClassDecl->implicitCopyAssignmentHasConstParam())
|
||||
ArgType = ArgType.withConst();
|
||||
ArgType = Context.getLValueReferenceType(ArgType);
|
||||
|
||||
|
@ -8580,70 +8507,6 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
|
|||
}
|
||||
}
|
||||
|
||||
/// Determine whether an implicit copy constructor for ClassDecl has a const
|
||||
/// argument.
|
||||
/// FIXME: It ought to be possible to store this on the record.
|
||||
static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl) {
|
||||
if (ClassDecl->isInvalidDecl())
|
||||
return true;
|
||||
|
||||
// C++ [class.copy]p5:
|
||||
// The implicitly-declared copy constructor for a class X will
|
||||
// have the form
|
||||
//
|
||||
// X::X(const X&)
|
||||
//
|
||||
// if
|
||||
// -- each direct or virtual base class B of X has a copy
|
||||
// constructor whose first parameter is of type const B& or
|
||||
// const volatile B&, and
|
||||
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
|
||||
BaseEnd = ClassDecl->bases_end();
|
||||
Base != BaseEnd; ++Base) {
|
||||
// Virtual bases are handled below.
|
||||
if (Base->isVirtual())
|
||||
continue;
|
||||
|
||||
CXXRecordDecl *BaseClassDecl
|
||||
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
|
||||
// FIXME: This lookup is wrong. If the copy ctor for a member or base is
|
||||
// ambiguous, we should still produce a constructor with a const-qualified
|
||||
// parameter.
|
||||
if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
|
||||
BaseEnd = ClassDecl->vbases_end();
|
||||
Base != BaseEnd; ++Base) {
|
||||
CXXRecordDecl *BaseClassDecl
|
||||
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
|
||||
if (!S.LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const))
|
||||
return false;
|
||||
}
|
||||
|
||||
// -- for all the nonstatic data members of X that are of a
|
||||
// class type M (or array thereof), each such class type
|
||||
// has a copy constructor whose first parameter is of type
|
||||
// const M& or const volatile M&.
|
||||
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
|
||||
FieldEnd = ClassDecl->field_end();
|
||||
Field != FieldEnd; ++Field) {
|
||||
QualType FieldType = S.Context.getBaseElementType(Field->getType());
|
||||
if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) {
|
||||
if (!S.LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the implicitly declared copy constructor will have
|
||||
// the form
|
||||
//
|
||||
// X::X(X&)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Sema::ImplicitExceptionSpecification
|
||||
Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) {
|
||||
CXXRecordDecl *ClassDecl = MD->getParent();
|
||||
|
@ -8708,7 +8571,7 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
|||
|
||||
QualType ClassType = Context.getTypeDeclType(ClassDecl);
|
||||
QualType ArgType = ClassType;
|
||||
bool Const = isImplicitCopyCtorArgConst(*this, ClassDecl);
|
||||
bool Const = ClassDecl->implicitCopyConstructorHasConstParam();
|
||||
if (Const)
|
||||
ArgType = ArgType.withConst();
|
||||
ArgType = Context.getLValueReferenceType(ArgType);
|
||||
|
|
|
@ -1120,6 +1120,10 @@ void ASTDeclReader::ReadCXXDefinitionData(
|
|||
Data.DeclaredCopyAssignment = Record[Idx++];
|
||||
Data.DeclaredMoveAssignment = Record[Idx++];
|
||||
Data.DeclaredDestructor = Record[Idx++];
|
||||
Data.ImplicitCopyConstructorHasConstParam = Record[Idx++];
|
||||
Data.ImplicitCopyAssignmentHasConstParam = Record[Idx++];
|
||||
Data.HasDeclaredCopyConstructorWithConstParam = Record[Idx++];
|
||||
Data.HasDeclaredCopyAssignmentWithConstParam = Record[Idx++];
|
||||
Data.FailedImplicitMoveConstructor = Record[Idx++];
|
||||
Data.FailedImplicitMoveAssignment = Record[Idx++];
|
||||
|
||||
|
|
|
@ -4611,6 +4611,10 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
|
|||
Record.push_back(Data.DeclaredCopyAssignment);
|
||||
Record.push_back(Data.DeclaredMoveAssignment);
|
||||
Record.push_back(Data.DeclaredDestructor);
|
||||
Record.push_back(Data.ImplicitCopyConstructorHasConstParam);
|
||||
Record.push_back(Data.ImplicitCopyAssignmentHasConstParam);
|
||||
Record.push_back(Data.HasDeclaredCopyConstructorWithConstParam);
|
||||
Record.push_back(Data.HasDeclaredCopyAssignmentWithConstParam);
|
||||
Record.push_back(Data.FailedImplicitMoveConstructor);
|
||||
Record.push_back(Data.FailedImplicitMoveAssignment);
|
||||
// IsLambda bit is already saved.
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// RUN: %clang_cc1 -std=c++11 %s -verify
|
||||
// expected-no-diagnostics
|
||||
|
||||
// C++98 [class.copy]p10 / C++11 [class.copy]p18.
|
||||
|
||||
// The implicitly-declared copy assignment operator for a class X will have the form
|
||||
// X& X::operator=(const X&)
|
||||
// if [every direct subobject] has a copy assignment operator whose first parameter is
|
||||
// of type 'const volatile[opt] T &' or 'T'. Otherwise, it will have the form
|
||||
// X &X::operator=(X&)
|
||||
|
||||
struct ConstCopy {
|
||||
ConstCopy &operator=(const ConstCopy &);
|
||||
};
|
||||
|
||||
struct NonConstCopy {
|
||||
NonConstCopy &operator=(NonConstCopy &);
|
||||
};
|
||||
|
||||
struct DeletedConstCopy {
|
||||
DeletedConstCopy &operator=(const DeletedConstCopy &) = delete;
|
||||
};
|
||||
|
||||
struct DeletedNonConstCopy {
|
||||
DeletedNonConstCopy &operator=(DeletedNonConstCopy &) = delete;
|
||||
};
|
||||
|
||||
struct ImplicitlyDeletedConstCopy {
|
||||
ImplicitlyDeletedConstCopy &operator=(ImplicitlyDeletedConstCopy &&);
|
||||
};
|
||||
|
||||
struct ByValueCopy {
|
||||
ByValueCopy &operator=(ByValueCopy);
|
||||
};
|
||||
|
||||
struct AmbiguousConstCopy {
|
||||
AmbiguousConstCopy &operator=(const AmbiguousConstCopy&);
|
||||
AmbiguousConstCopy &operator=(AmbiguousConstCopy);
|
||||
};
|
||||
|
||||
|
||||
struct A : ConstCopy {};
|
||||
struct B : NonConstCopy { ConstCopy a; };
|
||||
struct C : ConstCopy { NonConstCopy a; };
|
||||
struct D : DeletedConstCopy {};
|
||||
struct E : DeletedNonConstCopy {};
|
||||
struct F { ImplicitlyDeletedConstCopy a; };
|
||||
struct G : virtual B {};
|
||||
struct H : ByValueCopy {};
|
||||
struct I : AmbiguousConstCopy {};
|
||||
|
||||
struct Test {
|
||||
friend A &A::operator=(const A &);
|
||||
friend B &B::operator=(B &);
|
||||
friend C &C::operator=(C &);
|
||||
friend D &D::operator=(const D &);
|
||||
friend E &E::operator=(E &);
|
||||
friend F &F::operator=(const F &);
|
||||
friend G &G::operator=(G &);
|
||||
friend H &H::operator=(const H &);
|
||||
friend I &I::operator=(const I &);
|
||||
};
|
|
@ -1,6 +1,9 @@
|
|||
struct S {
|
||||
void m(int x);
|
||||
|
||||
S();
|
||||
S(const S&);
|
||||
|
||||
operator const char*();
|
||||
operator char*();
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// RUN: %clang_cc1 -x c++ -include %S/Inputs/cxx-method.h -verify %s
|
||||
// RUN: %clang_cc1 -x c++ -emit-pch %S/Inputs/cxx-method.h -o %t
|
||||
// RUN: %clang_cc1 -include-pch %t -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
@ -7,3 +8,8 @@ void S::m(int x) { }
|
|||
S::operator char *() { return 0; }
|
||||
|
||||
S::operator const char *() { return 0; }
|
||||
|
||||
struct T : S {};
|
||||
|
||||
const T a = T();
|
||||
T b(a);
|
||||
|
|
Loading…
Reference in New Issue