[cfe-commits] r168775 - in /cfe/trunk: include/clang/AST/DeclCXX.h lib/AST/ASTImporter.cpp lib/AST/DeclCXX.cpp lib/Sema/SemaDeclCXX.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp test/CXX/special/class.copy/p18-cxx11.cpp test/PCH/Inputs/cxx-method.h test/PCH/cxx-method.cpp

Richard Smith richard-llvm at metafoo.co.uk
Tue Nov 27 22:23:12 PST 2012


Author: rsmith
Date: Wed Nov 28 00:23:12 2012
New Revision: 168775

URL: http://llvm.org/viewvc/llvm-project?rev=168775&view=rev
Log:
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.

Added:
    cfe/trunk/test/CXX/special/class.copy/p18-cxx11.cpp
Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/lib/AST/ASTImporter.cpp
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/test/PCH/Inputs/cxx-method.h
    cfe/trunk/test/PCH/cxx-method.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Wed Nov 28 00:23:12 2012
@@ -494,6 +494,22 @@
     /// \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 @@
     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 @@
     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 @@
   /// 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 {

Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Wed Nov 28 00:23:12 2012
@@ -1950,6 +1950,14 @@
     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;

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Wed Nov 28 00:23:12 2012
@@ -55,9 +55,14 @@
     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 @@
           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 @@
     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 @@
       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 @@
         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 @@
         //   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 @@
           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 @@
     // 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 @@
 
     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 @@
       // 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 @@
           data().Conversions.addDecl(getASTContext(), Conversion);
       }
     }
-    
+
     return;
   }
   
@@ -902,6 +917,24 @@
           // 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.

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Nov 28 00:23:12 2012
@@ -4193,9 +4193,6 @@
                         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 @@
     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 @@
   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 @@
 
   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 @@
   }
 }
 
-/// 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 @@
 
   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);

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Wed Nov 28 00:23:12 2012
@@ -1120,6 +1120,10 @@
   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++];
 

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Wed Nov 28 00:23:12 2012
@@ -4611,6 +4611,10 @@
   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.

Added: cfe/trunk/test/CXX/special/class.copy/p18-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/special/class.copy/p18-cxx11.cpp?rev=168775&view=auto
==============================================================================
--- cfe/trunk/test/CXX/special/class.copy/p18-cxx11.cpp (added)
+++ cfe/trunk/test/CXX/special/class.copy/p18-cxx11.cpp Wed Nov 28 00:23:12 2012
@@ -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 &);
+};

Modified: cfe/trunk/test/PCH/Inputs/cxx-method.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/Inputs/cxx-method.h?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/test/PCH/Inputs/cxx-method.h (original)
+++ cfe/trunk/test/PCH/Inputs/cxx-method.h Wed Nov 28 00:23:12 2012
@@ -1,6 +1,9 @@
 struct S {
   void m(int x);
 
+  S();
+  S(const S&);
+
   operator const char*();
   operator char*();
 };

Modified: cfe/trunk/test/PCH/cxx-method.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx-method.cpp?rev=168775&r1=168774&r2=168775&view=diff
==============================================================================
--- cfe/trunk/test/PCH/cxx-method.cpp (original)
+++ cfe/trunk/test/PCH/cxx-method.cpp Wed Nov 28 00:23:12 2012
@@ -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 @@
 S::operator char *() { return 0; }
 
 S::operator const char *() { return 0; }
+
+struct T : S {};
+
+const T a = T();
+T b(a);





More information about the cfe-commits mailing list