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.
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 {

View File

@ -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;

View File

@ -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.

View File

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

View File

@ -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++];

View File

@ -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.

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 {
void m(int x);
S();
S(const S&);
operator const 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 -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);