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:
Richard Smith 2012-11-28 06:23:12 +00:00
parent 94c8348859
commit 1c33fe8fea
9 changed files with 276 additions and 248 deletions

View File

@ -494,6 +494,22 @@ class CXXRecordDecl : public RecordDecl {
/// \brief Whether we have already declared a destructor within the class. /// \brief Whether we have already declared a destructor within the class.
bool DeclaredDestructor : 1; 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 /// \brief Whether an implicit move constructor was attempted to be declared
/// but would have been deleted. /// but would have been deleted.
bool FailedImplicitMoveConstructor : 1; bool FailedImplicitMoveConstructor : 1;
@ -881,6 +897,20 @@ public:
return data().DeclaredCopyConstructor; 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- /// hasUserDeclaredMoveOperation - Whether this class has a user-
/// declared move constructor or assignment operator. When false, a /// declared move constructor or assignment operator. When false, a
/// move constructor and assignment operator may be implicitly declared. /// move constructor and assignment operator may be implicitly declared.
@ -943,6 +973,21 @@ public:
return data().DeclaredCopyAssignment; 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 /// \brief Determine whether this class has had a move assignment
/// declared by the user. /// declared by the user.
bool hasUserDeclaredMoveAssignment() const { bool hasUserDeclaredMoveAssignment() const {
@ -2126,7 +2171,7 @@ public:
/// constructor (C++ [class.copy]p2, which can be used to copy the /// constructor (C++ [class.copy]p2, which can be used to copy the
/// class. @p TypeQuals will be set to the qualifiers on the /// class. @p TypeQuals will be set to the qualifiers on the
/// argument type. For example, @p TypeQuals would be set to @c /// 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 /// @code
/// class X { /// class X {

View File

@ -1950,6 +1950,14 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
ToData.DeclaredCopyAssignment = FromData.DeclaredCopyAssignment; ToData.DeclaredCopyAssignment = FromData.DeclaredCopyAssignment;
ToData.DeclaredMoveAssignment = FromData.DeclaredMoveAssignment; ToData.DeclaredMoveAssignment = FromData.DeclaredMoveAssignment;
ToData.DeclaredDestructor = FromData.DeclaredDestructor; ToData.DeclaredDestructor = FromData.DeclaredDestructor;
ToData.ImplicitCopyConstructorHasConstParam
= FromData.ImplicitCopyConstructorHasConstParam;
ToData.ImplicitCopyAssignmentHasConstParam
= FromData.ImplicitCopyAssignmentHasConstParam;
ToData.HasDeclaredCopyConstructorWithConstParam
= FromData.HasDeclaredCopyConstructorWithConstParam;
ToData.HasDeclaredCopyAssignmentWithConstParam
= FromData.HasDeclaredCopyAssignmentWithConstParam;
ToData.FailedImplicitMoveConstructor ToData.FailedImplicitMoveConstructor
= FromData.FailedImplicitMoveConstructor; = FromData.FailedImplicitMoveConstructor;
ToData.FailedImplicitMoveAssignment = FromData.FailedImplicitMoveAssignment; ToData.FailedImplicitMoveAssignment = FromData.FailedImplicitMoveAssignment;

View File

@ -55,9 +55,14 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false), UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false),
DeclaredCopyConstructor(false), DeclaredMoveConstructor(false), DeclaredCopyConstructor(false), DeclaredMoveConstructor(false),
DeclaredCopyAssignment(false), DeclaredMoveAssignment(false), DeclaredCopyAssignment(false), DeclaredMoveAssignment(false),
DeclaredDestructor(false), FailedImplicitMoveConstructor(false), DeclaredDestructor(false),
FailedImplicitMoveAssignment(false), IsLambda(false), NumBases(0), ImplicitCopyConstructorHasConstParam(true),
NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) { 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 { CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
@ -184,15 +189,25 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
BaseClassDecl->vbases_begin(), BaseClassDecl->vbases_begin(),
E = BaseClassDecl->vbases_end(); VBase != E; ++VBase) { E = BaseClassDecl->vbases_end(); VBase != E; ++VBase) {
// Add this base if it's not already in the list. // 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); 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()) { if (Base->isVirtual()) {
// Add this base if it's not already in the list. // Add this base if it's not already in the list.
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType))) if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)))
VBases.push_back(Base); VBases.push_back(Base);
// C++0x [meta.unary.prop] is_empty: // C++0x [meta.unary.prop] is_empty:
// T is a class type, but not a union type, with ... no virtual base // T is a class type, but not a union type, with ... no virtual base
// classes // classes
@ -276,6 +291,22 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (!BaseClassDecl->hasIrrelevantDestructor()) if (!BaseClassDecl->hasIrrelevantDestructor())
data().HasIrrelevantDestructor = false; 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 // A class has an Objective-C object member if... or any of its bases
// has an Objective-C object member. // has an Objective-C object member.
if (BaseClassDecl->hasObjectMember()) if (BaseClassDecl->hasObjectMember())
@ -522,51 +553,25 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().IsStandardLayout = false; 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 // Notify the listener if an implicit member was added after the definition
// return early. // was completed.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { if (!isBeingDefined() && D->isImplicit())
if (Constructor->isDefaultConstructor()) { if (ASTMutationListener *L = getASTMutationListener())
data().DeclaredDefaultConstructor = true; L->AddedCXXImplicitMember(data().Definition, D);
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;
}
NotASpecialMember:; // Handle constructors.
// Any other implicit declarations are handled like normal declarations.
}
// Handle (user-declared) constructors.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
// Note that we have a user-declared constructor. if (!Constructor->isImplicit()) {
data().UserDeclaredConstructor = true; // 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 // Technically, "user-provided" is only defined for special member
// functions, but the intent of the standard is clearly that it should apply // functions, but the intent of the standard is clearly that it should apply
@ -581,17 +586,17 @@ NotASpecialMember:;
data().HasTrivialDefaultConstructor = false; data().HasTrivialDefaultConstructor = false;
data().UserProvidedDefaultConstructor = true; data().UserProvidedDefaultConstructor = true;
} }
if (Constructor->isConstexpr()) { if (Constructor->isConstexpr())
data().HasConstexprDefaultConstructor = true; data().HasConstexprDefaultConstructor = true;
data().HasConstexprNonCopyMoveConstructor = true;
}
} }
// Note when we have a user-declared copy or move constructor, which will // Note when we have a user-declared copy or move constructor, which will
// suppress the implicit declaration of those constructors. // suppress the implicit declaration of those constructors.
if (!FunTmpl) { if (!FunTmpl) {
if (Constructor->isCopyConstructor()) { unsigned Quals;
data().UserDeclaredCopyConstructor = true; if (Constructor->isCopyConstructor(Quals)) {
if (!Constructor->isImplicit())
data().UserDeclaredCopyConstructor = true;
data().DeclaredCopyConstructor = true; data().DeclaredCopyConstructor = true;
// C++0x [class.copy]p13: // C++0x [class.copy]p13:
@ -599,8 +604,12 @@ NotASpecialMember:;
// user-provided [...] // user-provided [...]
if (UserProvided) if (UserProvided)
data().HasTrivialCopyConstructor = false; data().HasTrivialCopyConstructor = false;
if (Quals & Qualifiers::Const)
data().HasDeclaredCopyConstructorWithConstParam = true;
} else if (Constructor->isMoveConstructor()) { } else if (Constructor->isMoveConstructor()) {
data().UserDeclaredMoveConstructor = true; if (!Constructor->isImplicit())
data().UserDeclaredMoveConstructor = true;
data().DeclaredMoveConstructor = true; data().DeclaredMoveConstructor = true;
// C++0x [class.copy]p13: // C++0x [class.copy]p13:
@ -610,11 +619,11 @@ NotASpecialMember:;
data().HasTrivialMoveConstructor = false; data().HasTrivialMoveConstructor = false;
} }
} }
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) {
// Record if we see any constexpr constructors which are neither copy // Record if we see any constexpr constructors which are neither copy
// nor move constructors. // nor move constructors.
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor())
data().HasConstexprNonCopyMoveConstructor = true; data().HasConstexprNonCopyMoveConstructor = true;
}
// C++ [dcl.init.aggr]p1: // C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class with no user-declared // An aggregate is an array or a class with no user-declared
@ -622,31 +631,29 @@ NotASpecialMember:;
// C++0x [dcl.init.aggr]p1: // C++0x [dcl.init.aggr]p1:
// An aggregate is an array or a class with no user-provided // An aggregate is an array or a class with no user-provided
// constructors [...]. // constructors [...].
if (!getASTContext().getLangOpts().CPlusPlus0x || UserProvided) if (getASTContext().getLangOpts().CPlusPlus0x
? UserProvided : !Constructor->isImplicit())
data().Aggregate = false; 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; return;
} }
// Handle (user-declared) destructors. // Handle destructors.
if (CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D)) { if (CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(D)) {
data().DeclaredDestructor = true; data().DeclaredDestructor = true;
data().UserDeclaredDestructor = true;
data().HasIrrelevantDestructor = false;
// C++ [class]p4: if (!DD->isImplicit()) {
// A POD-struct is an aggregate class that has [...] no user-defined data().UserDeclaredDestructor = true;
// destructor. data().HasIrrelevantDestructor = false;
// This bit is the C++03 POD bit, not the 0x one.
data().PlainOldData = false; // C++ [class]p4:
// A POD-struct is an aggregate class that has [...] no user-defined
// C++11 [class.dtor]p5: // 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 // A destructor is trivial if it is not user-provided and if
// -- the destructor is not virtual. // -- the destructor is not virtual.
if (DD->isUserProvided() || DD->isVirtual()) if (DD->isUserProvided() || DD->isVirtual())
@ -654,45 +661,53 @@ NotASpecialMember:;
return; return;
} }
// Handle (user-declared) member functions. // Handle member functions.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) { if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
if (Method->isCopyAssignmentOperator()) { 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. // Suppress the implicit declaration of a copy constructor.
data().UserDeclaredCopyAssignment = true;
data().DeclaredCopyAssignment = true; data().DeclaredCopyAssignment = true;
// C++0x [class.copy]p27: if (!Method->isImplicit()) {
// A copy/move assignment operator for class X is trivial if it is data().UserDeclaredCopyAssignment = true;
// neither user-provided nor deleted [...]
if (Method->isUserProvided())
data().HasTrivialCopyAssignment = false;
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. if (Method->isMoveAssignmentOperator()) {
data().UserDeclaredMoveAssignment = true;
data().DeclaredMoveAssignment = true; data().DeclaredMoveAssignment = true;
// C++0x [class.copy]p27: if (!Method->isImplicit()) {
// A copy/move assignment operator for class X is trivial if it is data().UserDeclaredMoveAssignment = true;
// neither user-provided nor deleted [...]
if (Method->isUserProvided()) // This is an extension in C++03 mode, but we'll keep consistency by
data().HasTrivialMoveAssignment = false; // 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. // Keep the list of conversion functions up-to-date.
@ -700,7 +715,7 @@ NotASpecialMember:;
// We don't record specializations. // We don't record specializations.
if (Conversion->getPrimaryTemplate()) if (Conversion->getPrimaryTemplate())
return; return;
// FIXME: We intentionally don't use the decl's access here because it // 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. // hasn't been set yet. That's really just a misdesign in Sema.
@ -718,7 +733,7 @@ NotASpecialMember:;
data().Conversions.addDecl(getASTContext(), Conversion); data().Conversions.addDecl(getASTContext(), Conversion);
} }
} }
return; return;
} }
@ -902,6 +917,24 @@ NotASpecialMember:;
// The standard requires any in-class initializer to be a constant // The standard requires any in-class initializer to be a constant
// expression. We consider this to be a defect. // expression. We consider this to be a defect.
data().DefaultedDefaultConstructorIsConstexpr = false; 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 { } else {
// Base element type of field is a non-class type. // Base element type of field is a non-class type.

View File

@ -4193,9 +4193,6 @@ void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD)
CanonicalFPT, ExceptSpec); CanonicalFPT, ExceptSpec);
} }
static bool isImplicitCopyCtorArgConst(Sema &S, CXXRecordDecl *ClassDecl);
static bool isImplicitCopyAssignmentArgConst(Sema &S, CXXRecordDecl *ClassDecl);
void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
CXXRecordDecl *RD = MD->getParent(); CXXRecordDecl *RD = MD->getParent();
CXXSpecialMember CSM = getSpecialMember(MD); CXXSpecialMember CSM = getSpecialMember(MD);
@ -4238,11 +4235,11 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
Trivial = RD->hasTrivialDefaultConstructor(); Trivial = RD->hasTrivialDefaultConstructor();
break; break;
case CXXCopyConstructor: case CXXCopyConstructor:
CanHaveConstParam = isImplicitCopyCtorArgConst(*this, RD); CanHaveConstParam = RD->implicitCopyConstructorHasConstParam();
Trivial = RD->hasTrivialCopyConstructor(); Trivial = RD->hasTrivialCopyConstructor();
break; break;
case CXXCopyAssignment: case CXXCopyAssignment:
CanHaveConstParam = isImplicitCopyAssignmentArgConst(*this, RD); CanHaveConstParam = RD->implicitCopyAssignmentHasConstParam();
Trivial = RD->hasTrivialCopyAssignment(); Trivial = RD->hasTrivialCopyAssignment();
break; break;
case CXXMoveConstructor: case CXXMoveConstructor:
@ -7727,76 +7724,6 @@ buildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T,
return Result; 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::ImplicitExceptionSpecification
Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) { Sema::ComputeDefaultedCopyAssignmentExceptionSpec(CXXMethodDecl *MD) {
CXXRecordDecl *ClassDecl = MD->getParent(); CXXRecordDecl *ClassDecl = MD->getParent();
@ -7868,7 +7795,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType ArgType = Context.getTypeDeclType(ClassDecl);
QualType RetType = Context.getLValueReferenceType(ArgType); QualType RetType = Context.getLValueReferenceType(ArgType);
if (isImplicitCopyAssignmentArgConst(*this, ClassDecl)) if (ClassDecl->implicitCopyAssignmentHasConstParam())
ArgType = ArgType.withConst(); ArgType = ArgType.withConst();
ArgType = Context.getLValueReferenceType(ArgType); 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::ImplicitExceptionSpecification
Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) { Sema::ComputeDefaultedCopyCtorExceptionSpec(CXXMethodDecl *MD) {
CXXRecordDecl *ClassDecl = MD->getParent(); CXXRecordDecl *ClassDecl = MD->getParent();
@ -8708,7 +8571,7 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ClassType = Context.getTypeDeclType(ClassDecl);
QualType ArgType = ClassType; QualType ArgType = ClassType;
bool Const = isImplicitCopyCtorArgConst(*this, ClassDecl); bool Const = ClassDecl->implicitCopyConstructorHasConstParam();
if (Const) if (Const)
ArgType = ArgType.withConst(); ArgType = ArgType.withConst();
ArgType = Context.getLValueReferenceType(ArgType); ArgType = Context.getLValueReferenceType(ArgType);

View File

@ -1120,6 +1120,10 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.DeclaredCopyAssignment = Record[Idx++]; Data.DeclaredCopyAssignment = Record[Idx++];
Data.DeclaredMoveAssignment = Record[Idx++]; Data.DeclaredMoveAssignment = Record[Idx++];
Data.DeclaredDestructor = 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.FailedImplicitMoveConstructor = Record[Idx++];
Data.FailedImplicitMoveAssignment = Record[Idx++]; Data.FailedImplicitMoveAssignment = Record[Idx++];

View File

@ -4611,6 +4611,10 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
Record.push_back(Data.DeclaredCopyAssignment); Record.push_back(Data.DeclaredCopyAssignment);
Record.push_back(Data.DeclaredMoveAssignment); Record.push_back(Data.DeclaredMoveAssignment);
Record.push_back(Data.DeclaredDestructor); 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.FailedImplicitMoveConstructor);
Record.push_back(Data.FailedImplicitMoveAssignment); Record.push_back(Data.FailedImplicitMoveAssignment);
// IsLambda bit is already saved. // IsLambda bit is already saved.

View File

@ -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 &);
};

View File

@ -1,6 +1,9 @@
struct S { struct S {
void m(int x); void m(int x);
S();
S(const S&);
operator const char*(); operator const char*();
operator char*(); operator char*();
}; };

View File

@ -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 -x c++ -emit-pch %S/Inputs/cxx-method.h -o %t
// RUN: %clang_cc1 -include-pch %t -verify %s // RUN: %clang_cc1 -include-pch %t -verify %s
// expected-no-diagnostics // expected-no-diagnostics
@ -7,3 +8,8 @@ void S::m(int x) { }
S::operator char *() { return 0; } S::operator char *() { return 0; }
S::operator const char *() { return 0; } S::operator const char *() { return 0; }
struct T : S {};
const T a = T();
T b(a);