diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 47 | ||||
-rw-r--r-- | lib/AST/ASTImporter.cpp | 8 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 247 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 145 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderDecl.cpp | 4 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 4 | ||||
-rw-r--r-- | test/CXX/special/class.copy/p18-cxx11.cpp | 62 | ||||
-rw-r--r-- | test/PCH/Inputs/cxx-method.h | 3 | ||||
-rw-r--r-- | test/PCH/cxx-method.cpp | 6 |
9 files changed, 277 insertions, 249 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 7e89e1e21a..c949dbbed0 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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 { diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 906cc3b4d0..054cd962f3 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -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; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index d661aa04af..a3c0bab8b8 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -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; - } -NotASpecialMember:; - // Any other implicit declarations are handled like normal declarations. - } - - // Handle (user-declared) constructors. + // 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); + + // 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; + + // 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; + } - return; + 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. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 86da9e907b..b8056795e9 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index c42944df63..751b989c4d 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -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++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 41665d8969..d57dba8be1 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -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. diff --git a/test/CXX/special/class.copy/p18-cxx11.cpp b/test/CXX/special/class.copy/p18-cxx11.cpp new file mode 100644 index 0000000000..7b09dd679f --- /dev/null +++ b/test/CXX/special/class.copy/p18-cxx11.cpp @@ -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 &); +}; diff --git a/test/PCH/Inputs/cxx-method.h b/test/PCH/Inputs/cxx-method.h index 6adb859170..d5d56fed05 100644 --- a/test/PCH/Inputs/cxx-method.h +++ b/test/PCH/Inputs/cxx-method.h @@ -1,6 +1,9 @@ struct S { void m(int x); + S(); + S(const S&); + operator const char*(); operator char*(); }; diff --git a/test/PCH/cxx-method.cpp b/test/PCH/cxx-method.cpp index 40490ea681..c24ad92975 100644 --- a/test/PCH/cxx-method.cpp +++ b/test/PCH/cxx-method.cpp @@ -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); |