]> granicus.if.org Git - clang/commitdiff
Finish implementing 'selected constructor' rules for triviality in C++11. In
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 8 Dec 2012 08:32:28 +0000 (08:32 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 8 Dec 2012 08:32:28 +0000 (08:32 +0000)
the cases where we can't determine whether special members would be trivial
while building the class, we eagerly declare those special members. The impact
of this is bounded, since it does not trigger implicit declarations of special
members in classes which merely *use* those classes.

In order to determine whether we need to apply this rule, we also need to
eagerly declare move operations and destructors in cases where they might be
deleted. If a move operation were supposed to be deleted, it would instead
be suppressed, and we could need overload resolution to determine if we fall
back to a trivial copy operation. If a destructor were implicitly deleted,
it would cause the move constructor of any derived classes to be suppressed.

As discussed on cxx-abi-dev, C++11's selected constructor rules are also
retroactively applied as a defect resolution in C++03 mode, in order to
identify that class B has a non-trivial copy constructor (since it calls
A's constructor template, not A's copy constructor):

struct A { template<typename T> A(T &); };
struct B { mutable A a; };

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@169673 91177308-0d34-0410-b5e6-96231b3b80d8

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/p12-0x.cpp
test/CXX/special/class.copy/p25-0x.cpp
test/PCH/cxx0x-default-delete.cpp
test/SemaCXX/type-traits.cpp

index bf6d62333bcbcf31e5b0bd5172e30faab6b3b8dc..3edb5839ce90b0dca516c4be766bc32cd34c5e03 100644 (file)
@@ -358,6 +358,22 @@ class CXXRecordDecl : public RecordDecl {
     /// is illegal in C++98 even if the class has a trivial default constructor.
     bool HasUninitializedReferenceMember : 1;
 
+    /// \brief These flags are \c true if a defaulted corresponding special
+    /// member can't be fully analyzed without performing overload resolution.
+    /// @{
+    bool NeedOverloadResolutionForMoveConstructor : 1;
+    bool NeedOverloadResolutionForMoveAssignment : 1;
+    bool NeedOverloadResolutionForDestructor : 1;
+    /// @}
+
+    /// \brief These flags are \c true if an implicit defaulted corresponding
+    /// special member would be defined as deleted.
+    /// @{
+    bool DefaultedMoveConstructorIsDeleted : 1;
+    bool DefaultedMoveAssignmentIsDeleted : 1;
+    bool DefaultedDestructorIsDeleted : 1;
+    /// @}
+
     /// \brief The trivial special members which this class has, per
     /// C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25,
     /// C++11 [class.dtor]p5, or would have if the member were not suppressed.
@@ -566,6 +582,10 @@ class CXXRecordDecl : public RecordDecl {
   friend class DeclContext;
   friend class LambdaExpr;
 
+  /// \brief Called from setBases and addedMember to notify the class that a
+  /// direct or virtual base class or a member of class type has been added.
+  void addedClassSubobject(CXXRecordDecl *Base);
+
   /// \brief Notify the class that member has been added.
   ///
   /// This routine helps maintain information about the class based on which
@@ -733,6 +753,23 @@ public:
     return data().FirstFriend != 0;
   }
 
+  /// \brief \c true if we know for sure that this class has a single,
+  /// accessible, unambiguous move constructor that is not deleted.
+  bool hasSimpleMoveConstructor() const {
+    return !hasUserDeclaredMoveConstructor() && hasMoveConstructor();
+  }
+  /// \brief \c true if we know for sure that this class has a single,
+  /// accessible, unambiguous move assignment operator that is not deleted.
+  bool hasSimpleMoveAssignment() const {
+    return !hasUserDeclaredMoveAssignment() && hasMoveAssignment();
+  }
+  /// \brief \c true if we know for sure that this class has an accessible
+  /// destructor that is not deleted.
+  bool hasSimpleDestructor() const {
+    return !hasUserDeclaredDestructor() &&
+           !data().DefaultedDestructorIsDeleted;
+  }
+
   /// \brief Determine whether this class has any default constructors.
   bool hasDefaultConstructor() const {
     return (data().DeclaredSpecialMembers & SMF_DefaultConstructor) ||
@@ -774,6 +811,12 @@ public:
     return !(data().DeclaredSpecialMembers & SMF_CopyConstructor);
   }
 
+  /// \brief Determine whether we need to eagerly declare a defaulted copy
+  /// constructor for this class.
+  bool needsOverloadResolutionForCopyConstructor() const {
+    return data().HasMutableFields;
+  }
+
   /// \brief Determine whether an implicit copy constructor for this type
   /// would have a parameter with a const-qualified reference type.
   bool implicitCopyConstructorHasConstParam() const {
@@ -803,8 +846,6 @@ public:
   }
 
   /// \brief Determine whether this class has a move constructor.
-  /// FIXME: This can be wrong if the implicit move constructor would be
-  /// deleted.
   bool hasMoveConstructor() const {
     return (data().DeclaredSpecialMembers & SMF_MoveConstructor) ||
            needsImplicitMoveConstructor();
@@ -824,17 +865,20 @@ public:
 
   /// \brief Determine whether this class should get an implicit move
   /// constructor or if any existing special member function inhibits this.
-  ///
-  /// Covers all bullets of C++0x [class.copy]p9 except the last, that the
-  /// constructor wouldn't be deleted, which is only looked up from a cached
-  /// result.
   bool needsImplicitMoveConstructor() const {
     return !hasFailedImplicitMoveConstructor() &&
            !(data().DeclaredSpecialMembers & SMF_MoveConstructor) &&
            !hasUserDeclaredCopyConstructor() &&
            !hasUserDeclaredCopyAssignment() &&
            !hasUserDeclaredMoveAssignment() &&
-           !hasUserDeclaredDestructor();
+           !hasUserDeclaredDestructor() &&
+           !data().DefaultedMoveConstructorIsDeleted;
+  }
+
+  /// \brief Determine whether we need to eagerly declare a defaulted move
+  /// constructor for this class.
+  bool needsOverloadResolutionForMoveConstructor() const {
+    return data().NeedOverloadResolutionForMoveConstructor;
   }
 
   /// hasUserDeclaredCopyAssignment - Whether this class has a
@@ -850,6 +894,12 @@ public:
     return !(data().DeclaredSpecialMembers & SMF_CopyAssignment);
   }
 
+  /// \brief Determine whether we need to eagerly declare a defaulted copy
+  /// assignment operator for this class.
+  bool needsOverloadResolutionForCopyAssignment() const {
+    return data().HasMutableFields;
+  }
+
   /// \brief Determine whether an implicit copy assignment operator for this
   /// type would have a parameter with a const-qualified reference type.
   bool implicitCopyAssignmentHasConstParam() const {
@@ -872,7 +922,6 @@ public:
   }
 
   /// \brief Determine whether this class has a move assignment operator.
-  /// FIXME: This can be wrong if the implicit move assignment would be deleted.
   bool hasMoveAssignment() const {
     return (data().DeclaredSpecialMembers & SMF_MoveAssignment) ||
            needsImplicitMoveAssignment();
@@ -893,16 +942,20 @@ public:
   /// \brief Determine whether this class should get an implicit move
   /// assignment operator or if any existing special member function inhibits
   /// this.
-  ///
-  /// Covers all bullets of C++0x [class.copy]p20 except the last, that the
-  /// constructor wouldn't be deleted.
   bool needsImplicitMoveAssignment() const {
     return !hasFailedImplicitMoveAssignment() &&
            !(data().DeclaredSpecialMembers & SMF_MoveAssignment) &&
            !hasUserDeclaredCopyConstructor() &&
            !hasUserDeclaredCopyAssignment() &&
            !hasUserDeclaredMoveConstructor() &&
-           !hasUserDeclaredDestructor();
+           !hasUserDeclaredDestructor() &&
+           !data().DefaultedMoveAssignmentIsDeleted;
+  }
+
+  /// \brief Determine whether we need to eagerly declare a move assignment
+  /// operator for this class.
+  bool needsOverloadResolutionForMoveAssignment() const {
+    return data().NeedOverloadResolutionForMoveAssignment;
   }
 
   /// hasUserDeclaredDestructor - Whether this class has a
@@ -918,9 +971,15 @@ public:
     return !(data().DeclaredSpecialMembers & SMF_Destructor);
   }
 
+  /// \brief Determine whether we need to eagerly declare a destructor for this
+  /// class.
+  bool needsOverloadResolutionForDestructor() const {
+    return data().NeedOverloadResolutionForDestructor;
+  }
+
   /// \brief Determine whether this class describes a lambda function object.
   bool isLambda() const { return hasDefinition() && data().IsLambda; }
-  
+
   /// \brief For a closure type, retrieve the mapping from captured
   /// variables and this to the non-static data members that store the
   /// values or references of the captures.
@@ -1067,8 +1126,6 @@ public:
 
   /// \brief Determine whether this class has a trivial move constructor
   /// (C++11 [class.copy]p12)
-  /// FIXME: This can be wrong if the implicit move constructor would be
-  /// deleted.
   bool hasTrivialMoveConstructor() const {
     return hasMoveConstructor() &&
            (data().HasTrivialSpecialMembers & SMF_MoveConstructor);
@@ -1076,8 +1133,6 @@ public:
 
   /// \brief Determine whether this class has a non-trivial move constructor
   /// (C++11 [class.copy]p12)
-  /// FIXME: This can be wrong if the implicit move constructor would be
-  /// deleted.
   bool hasNonTrivialMoveConstructor() const {
     return (data().DeclaredNonTrivialSpecialMembers & SMF_MoveConstructor) ||
            (needsImplicitMoveConstructor() &&
@@ -1099,7 +1154,6 @@ public:
 
   /// \brief Determine whether this class has a trivial move assignment operator
   /// (C++11 [class.copy]p25)
-  /// FIXME: This can be wrong if the implicit move assignment would be deleted.
   bool hasTrivialMoveAssignment() const {
     return hasMoveAssignment() &&
            (data().HasTrivialSpecialMembers & SMF_MoveAssignment);
@@ -1107,7 +1161,6 @@ public:
 
   /// \brief Determine whether this class has a non-trivial move assignment
   /// operator (C++11 [class.copy]p25)
-  /// FIXME: This can be wrong if the implicit move assignment would be deleted.
   bool hasNonTrivialMoveAssignment() const {
     return (data().DeclaredNonTrivialSpecialMembers & SMF_MoveAssignment) ||
            (needsImplicitMoveAssignment() &&
index 35bc993f73f9862b82c78f869d3c9751b3a3796f..f288953df615bfd369f607318f3cd19435c58814 100644 (file)
@@ -1923,6 +1923,17 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
     ToData.HasInClassInitializer = FromData.HasInClassInitializer;
     ToData.HasUninitializedReferenceMember
       = FromData.HasUninitializedReferenceMember;
+    ToData.NeedOverloadResolutionForMoveConstructor
+      = FromData.NeedOverloadResolutionForMoveConstructor;
+    ToData.NeedOverloadResolutionForMoveAssignment
+      = FromData.NeedOverloadResolutionForMoveAssignment;
+    ToData.NeedOverloadResolutionForDestructor
+      = FromData.NeedOverloadResolutionForDestructor;
+    ToData.DefaultedMoveConstructorIsDeleted
+      = FromData.DefaultedMoveConstructorIsDeleted;
+    ToData.DefaultedMoveAssignmentIsDeleted
+      = FromData.DefaultedMoveAssignmentIsDeleted;
+    ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted;
     ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers;
     ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor;
     ToData.HasConstexprNonCopyMoveConstructor
index 712480655e2b30ee1d8381d1198b332a56715d47..73d6ddd6a6d8f82b219d8ebc29f819d51b15c510 100644 (file)
@@ -42,6 +42,12 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
     HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false),
     HasMutableFields(false), HasOnlyCMembers(true),
     HasInClassInitializer(false), HasUninitializedReferenceMember(false),
+    NeedOverloadResolutionForMoveConstructor(false),
+    NeedOverloadResolutionForMoveAssignment(false),
+    NeedOverloadResolutionForDestructor(false),
+    DefaultedMoveConstructorIsDeleted(false),
+    DefaultedMoveAssignmentIsDeleted(false),
+    DefaultedDestructorIsDeleted(false),
     HasTrivialSpecialMembers(SMF_All),
     DeclaredNonTrivialSpecialMembers(0),
     HasIrrelevantDestructor(true),
@@ -235,11 +241,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
       //    [...]
       //    -- the constructor selected to copy/move each direct base class
       //       subobject is trivial, and
-      // FIXME: C++0x: We need to only consider the selected constructor
-      // instead of all of them. For now, we treat a move constructor as being
-      // non-trivial if it calls anything other than a trivial move constructor.
       if (!BaseClassDecl->hasTrivialCopyConstructor())
         data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
+      // If the base class doesn't have a simple move constructor, we'll eagerly
+      // declare it and perform overload resolution to determine which function
+      // it actually calls. If it does have a simple move constructor, this
+      // check is correct.
       if (!BaseClassDecl->hasTrivialMoveConstructor())
         data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
 
@@ -248,10 +255,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
       //    [...]
       //    -- the assignment operator selected to copy/move each direct base
       //       class subobject is trivial, and
-      // FIXME: C++0x: We need to only consider the selected operator instead
-      // of all of them.
       if (!BaseClassDecl->hasTrivialCopyAssignment())
         data().HasTrivialSpecialMembers &= ~SMF_CopyAssignment;
+      // If the base class doesn't have a simple move assignment, we'll eagerly
+      // declare it and perform overload resolution to determine which function
+      // it actually calls. If it does have a simple move assignment, this
+      // check is correct.
       if (!BaseClassDecl->hasTrivialMoveAssignment())
         data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
 
@@ -299,6 +308,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
 
     if (BaseClassDecl->hasUninitializedReferenceMember())
       data().HasUninitializedReferenceMember = true;
+
+    addedClassSubobject(BaseClassDecl);
   }
   
   if (VBases.empty())
@@ -307,8 +318,44 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
   // Create base specifier for any direct or indirect virtual bases.
   data().VBases = new (C) CXXBaseSpecifier[VBases.size()];
   data().NumVBases = VBases.size();
-  for (int I = 0, E = VBases.size(); I != E; ++I)
+  for (int I = 0, E = VBases.size(); I != E; ++I) {
+    QualType Type = VBases[I]->getType();
+    if (!Type->isDependentType())
+      addedClassSubobject(Type->getAsCXXRecordDecl());
     data().getVBases()[I] = *VBases[I];
+  }
+}
+
+void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
+  // C++11 [class.copy]p11:
+  //   A defaulted copy/move constructor for a class X is defined as
+  //   deleted if X has:
+  //    -- a direct or virtual base class B that cannot be copied/moved [...]
+  //    -- a non-static data member of class type M (or array thereof)
+  //       that cannot be copied or moved [...]
+  if (!Subobj->hasSimpleMoveConstructor())
+    data().NeedOverloadResolutionForMoveConstructor = true;
+
+  // C++11 [class.copy]p23:
+  //   A defaulted copy/move assignment operator for a class X is defined as
+  //   deleted if X has:
+  //    -- a direct or virtual base class B that cannot be copied/moved [...]
+  //    -- a non-static data member of class type M (or array thereof)
+  //        that cannot be copied or moved [...]
+  if (!Subobj->hasSimpleMoveAssignment())
+    data().NeedOverloadResolutionForMoveAssignment = true;
+
+  // C++11 [class.ctor]p5, C++11 [class.copy]p11, C++11 [class.dtor]p5:
+  //   A defaulted [ctor or dtor] for a class X is defined as
+  //   deleted if X has:
+  //    -- any direct or virtual base class [...] has a type with a destructor
+  //       that is deleted or inaccessible from the defaulted [ctor or dtor].
+  //    -- any non-static data member has a type with a destructor
+  //       that is deleted or inaccessible from the defaulted [ctor or dtor].
+  if (!Subobj->hasSimpleDestructor()) {
+    data().NeedOverloadResolutionForMoveConstructor = true;
+    data().NeedOverloadResolutionForDestructor = true;
+  }
 }
 
 /// Callback function for CXXRecordDecl::forallBases that acknowledges
@@ -636,9 +683,32 @@ void CXXRecordDecl::addedMember(Decl *D) {
       data().PlainOldData = false;
     }
 
+    // C++11 [class.copy]p23:
+    //   A defaulted copy/move assignment operator for a class X is defined
+    //   as deleted if X has:
+    //    -- a non-static data member of reference type
+    if (T->isReferenceType())
+      data().DefaultedMoveAssignmentIsDeleted = true;
+
     if (const RecordType *RecordTy = T->getAs<RecordType>()) {
       CXXRecordDecl* FieldRec = cast<CXXRecordDecl>(RecordTy->getDecl());
       if (FieldRec->getDefinition()) {
+        addedClassSubobject(FieldRec);
+
+        // C++11 [class.ctor]p5, C++11 [class.copy]p11:
+        //   A defaulted [special member] for a class X is defined as
+        //   deleted if:
+        //    -- X is a union-like class that has a variant member with a
+        //       non-trivial [corresponding special member]
+        if (isUnion()) {
+          if (FieldRec->hasNonTrivialMoveConstructor())
+            data().DefaultedMoveConstructorIsDeleted = true;
+          if (FieldRec->hasNonTrivialMoveAssignment())
+            data().DefaultedMoveAssignmentIsDeleted = true;
+          if (FieldRec->hasNonTrivialDestructor())
+            data().DefaultedDestructorIsDeleted = true;
+        }
+
         // C++0x [class.ctor]p5:
         //   A default constructor is trivial [...] if:
         //    -- for all the non-static data members of its class that are of
@@ -653,9 +723,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
         //    -- for each non-static data member of X that is of class type (or
         //       an array thereof), the constructor selected to copy/move that
         //       member is trivial;
-        // FIXME: C++0x: We don't correctly model 'selected' constructors.
         if (!FieldRec->hasTrivialCopyConstructor())
           data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
+        // If the field doesn't have a simple move constructor, we'll eagerly
+        // declare the move constructor for this class and we'll decide whether
+        // it's trivial then.
         if (!FieldRec->hasTrivialMoveConstructor())
           data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
 
@@ -665,9 +737,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
         //    -- for each non-static data member of X that is of class type (or
         //       an array thereof), the assignment operator selected to
         //       copy/move that member is trivial;
-        // FIXME: C++0x: We don't correctly model 'selected' operators.
         if (!FieldRec->hasTrivialCopyAssignment())
           data().HasTrivialSpecialMembers &= ~SMF_CopyAssignment;
+        // If the field doesn't have a simple move assignment, we'll eagerly
+        // declare the move assignment for this class and we'll decide whether
+        // it's trivial then.
         if (!FieldRec->hasTrivialMoveAssignment())
           data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
 
@@ -755,6 +829,14 @@ void CXXRecordDecl::addedMember(Decl *D) {
       if (!T->isLiteralType() ||
           (!Field->hasInClassInitializer() && !isUnion()))
         data().DefaultedDefaultConstructorIsConstexpr = false;
+
+      // C++11 [class.copy]p23:
+      //   A defaulted copy/move assignment operator for a class X is defined
+      //   as deleted if X has:
+      //    -- a non-static data member of const non-class type (or array
+      //       thereof)
+      if (T.isConstQualified())
+        data().DefaultedMoveAssignmentIsDeleted = true;
     }
 
     // C++0x [class]p7:
index 8666453c311c35032b3b9ddfbec7e5eeb4314254..5cca43b548083934ebd05e4d2a7b0afc1fed954a 100644 (file)
@@ -5036,12 +5036,6 @@ void Sema::DiagnoseNontrivial(const CXXRecordDecl *RD, CXXSpecialMember CSM) {
 /// C++11 [class.copy]p25, and C++11 [class.dtor]p5.
 bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
                                   bool Diagnose) {
-  // Note that we can't work out CSM for ourselves. Consider this:
-  //
-  //   struct S { S(int); S(const S&=0) = delete; };
-  //
-  // The same function is a trivial copy constructor but a non-trivial default
-  // constructor.
   assert(!MD->isUserProvided() && CSM != CXXInvalid && "not special enough");
 
   CXXRecordDecl *RD = MD->getParent();
@@ -5337,20 +5331,32 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
   if (!ClassDecl->hasUserDeclaredConstructor())
     ++ASTContext::NumImplicitDefaultConstructors;
 
-  if (!ClassDecl->hasUserDeclaredCopyConstructor())
+  if (!ClassDecl->hasUserDeclaredCopyConstructor()) {
     ++ASTContext::NumImplicitCopyConstructors;
 
-  if (getLangOpts().CPlusPlus0x && ClassDecl->needsImplicitMoveConstructor())
+    // If the properties or semantics of the copy constructor couldn't be
+    // determined while the class was being declared, force a declaration
+    // of it now.
+    if (ClassDecl->needsOverloadResolutionForCopyConstructor())
+      DeclareImplicitCopyConstructor(ClassDecl);
+  }
+
+  if (getLangOpts().CPlusPlus0x && ClassDecl->needsImplicitMoveConstructor()) {
     ++ASTContext::NumImplicitMoveConstructors;
 
+    if (ClassDecl->needsOverloadResolutionForMoveConstructor())
+      DeclareImplicitMoveConstructor(ClassDecl);
+  }
+
   if (!ClassDecl->hasUserDeclaredCopyAssignment()) {
     ++ASTContext::NumImplicitCopyAssignmentOperators;
-    
-    // If we have a dynamic class, then the copy assignment operator may be 
+
+    // If we have a dynamic class, then the copy assignment operator may be
     // virtual, so we have to declare it immediately. This ensures that, e.g.,
-    // it shows up in the right place in the vtable and that we diagnose 
-    // problems with the implicit exception specification.    
-    if (ClassDecl->isDynamicClass())
+    // it shows up in the right place in the vtable and that we diagnose
+    // problems with the implicit exception specification.
+    if (ClassDecl->isDynamicClass() ||
+        ClassDecl->needsOverloadResolutionForCopyAssignment())
       DeclareImplicitCopyAssignment(ClassDecl);
   }
 
@@ -5358,18 +5364,20 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
     ++ASTContext::NumImplicitMoveAssignmentOperators;
 
     // Likewise for the move assignment operator.
-    if (ClassDecl->isDynamicClass())
+    if (ClassDecl->isDynamicClass() ||
+        ClassDecl->needsOverloadResolutionForMoveAssignment())
       DeclareImplicitMoveAssignment(ClassDecl);
   }
 
   if (!ClassDecl->hasUserDeclaredDestructor()) {
     ++ASTContext::NumImplicitDestructors;
-    
-    // If we have a dynamic class, then the destructor may be virtual, so we 
+
+    // If we have a dynamic class, then the destructor may be virtual, so we
     // have to declare the destructor immediately. This ensures that, e.g., it
     // shows up in the right place in the vtable and that we diagnose problems
     // with the implicit exception specification.
-    if (ClassDecl->isDynamicClass())
+    if (ClassDecl->isDynamicClass() ||
+        ClassDecl->needsOverloadResolutionForDestructor())
       DeclareImplicitDestructor(ClassDecl);
   }
 }
@@ -7434,7 +7442,6 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
   DefaultCon->setAccess(AS_public);
   DefaultCon->setDefaulted();
   DefaultCon->setImplicit();
-  DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor());
 
   // Build an exception specification pointing back at this constructor.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -7442,16 +7449,20 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
   EPI.ExceptionSpecDecl = DefaultCon;
   DefaultCon->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
 
+  // We don't need to use SpecialMemberIsTrivial here; triviality for default
+  // constructors is easy to compute.
+  DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor());
+
+  if (ShouldDeleteSpecialMember(DefaultCon, CXXDefaultConstructor))
+    DefaultCon->setDeletedAsWritten();
+
   // Note that we have declared this constructor.
   ++ASTContext::NumImplicitDefaultConstructorsDeclared;
-  
+
   if (Scope *S = getScopeForContext(ClassDecl))
     PushOnScopeChains(DefaultCon, S, false);
   ClassDecl->addDecl(DefaultCon);
 
-  if (ShouldDeleteSpecialMember(DefaultCon, CXXDefaultConstructor))
-    DefaultCon->setDeletedAsWritten();
-  
   return DefaultCon;
 }
 
@@ -7785,7 +7796,6 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
   Destructor->setAccess(AS_public);
   Destructor->setDefaulted();
   Destructor->setImplicit();
-  Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
 
   // Build an exception specification pointing back at this destructor.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -7793,6 +7803,15 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
   EPI.ExceptionSpecDecl = Destructor;
   Destructor->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
 
+  AddOverriddenMethods(ClassDecl, Destructor);
+
+  // We don't need to use SpecialMemberIsTrivial here; triviality for
+  // destructors is easy to compute.
+  Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
+
+  if (ShouldDeleteSpecialMember(Destructor, CXXDestructor))
+    Destructor->setDeletedAsWritten();
+
   // Note that we have declared this destructor.
   ++ASTContext::NumImplicitDestructorsDeclared;
 
@@ -7801,11 +7820,6 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
     PushOnScopeChains(Destructor, S, false);
   ClassDecl->addDecl(Destructor);
 
-  AddOverriddenMethods(ClassDecl, Destructor);
-
-  if (ShouldDeleteSpecialMember(Destructor, CXXDestructor))
-    Destructor->setDeletedAsWritten();
-
   return Destructor;
 }
 
@@ -8278,7 +8292,6 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
   CopyAssignment->setAccess(AS_public);
   CopyAssignment->setDefaulted();
   CopyAssignment->setImplicit();
-  CopyAssignment->setTrivial(ClassDecl->hasTrivialCopyAssignment());
 
   // Build an exception specification pointing back at this member.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -8293,14 +8306,14 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
                                                SC_None,
                                                SC_None, 0);
   CopyAssignment->setParams(FromParam);
-  
-  // Note that we have added this copy-assignment operator.
-  ++ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
 
-  if (Scope *S = getScopeForContext(ClassDecl))
-    PushOnScopeChains(CopyAssignment, S, false);
-  ClassDecl->addDecl(CopyAssignment);
-  
+  AddOverriddenMethods(ClassDecl, CopyAssignment);
+
+  CopyAssignment->setTrivial(
+    ClassDecl->needsOverloadResolutionForCopyAssignment()
+      ? SpecialMemberIsTrivial(CopyAssignment, CXXCopyAssignment)
+      : ClassDecl->hasTrivialCopyAssignment());
+
   // C++0x [class.copy]p19:
   //   ....  If the class definition does not explicitly declare a copy
   //   assignment operator, there is no user-declared move constructor, and
@@ -8309,7 +8322,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
   if (ShouldDeleteSpecialMember(CopyAssignment, CXXCopyAssignment))
     CopyAssignment->setDeletedAsWritten();
 
-  AddOverriddenMethods(ClassDecl, CopyAssignment);
+  // Note that we have added this copy-assignment operator.
+  ++ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
+
+  if (Scope *S = getScopeForContext(ClassDecl))
+    PushOnScopeChains(CopyAssignment, S, false);
+  ClassDecl->addDecl(CopyAssignment);
+
   return CopyAssignment;
 }
 
@@ -8723,7 +8742,6 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
   MoveAssignment->setAccess(AS_public);
   MoveAssignment->setDefaulted();
   MoveAssignment->setImplicit();
-  MoveAssignment->setTrivial(ClassDecl->hasTrivialMoveAssignment());
 
   // Build an exception specification pointing back at this member.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -8739,8 +8757,12 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
                                                SC_None, 0);
   MoveAssignment->setParams(FromParam);
 
-  // Note that we have added this copy-assignment operator.
-  ++ASTContext::NumImplicitMoveAssignmentOperatorsDeclared;
+  AddOverriddenMethods(ClassDecl, MoveAssignment);
+
+  MoveAssignment->setTrivial(
+    ClassDecl->needsOverloadResolutionForMoveAssignment()
+      ? SpecialMemberIsTrivial(MoveAssignment, CXXMoveAssignment)
+      : ClassDecl->hasTrivialMoveAssignment());
 
   // C++0x [class.copy]p9:
   //   If the definition of a class X does not explicitly declare a move
@@ -8756,11 +8778,13 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) {
     return 0;
   }
 
+  // Note that we have added this copy-assignment operator.
+  ++ASTContext::NumImplicitMoveAssignmentOperatorsDeclared;
+
   if (Scope *S = getScopeForContext(ClassDecl))
     PushOnScopeChains(MoveAssignment, S, false);
   ClassDecl->addDecl(MoveAssignment);
 
-  AddOverriddenMethods(ClassDecl, MoveAssignment);
   return MoveAssignment;
 }
 
@@ -9071,7 +9095,6 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
       Constexpr);
   CopyConstructor->setAccess(AS_public);
   CopyConstructor->setDefaulted();
-  CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor());
 
   // Build an exception specification pointing back at this member.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -9080,9 +9103,6 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
   CopyConstructor->setType(
       Context.getFunctionType(Context.VoidTy, &ArgType, 1, EPI));
 
-  // Note that we have declared this constructor.
-  ++ASTContext::NumImplicitCopyConstructorsDeclared;
-  
   // Add the parameter to the constructor.
   ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyConstructor,
                                                ClassLoc, ClassLoc,
@@ -9092,9 +9112,10 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
                                                SC_None, 0);
   CopyConstructor->setParams(FromParam);
 
-  if (Scope *S = getScopeForContext(ClassDecl))
-    PushOnScopeChains(CopyConstructor, S, false);
-  ClassDecl->addDecl(CopyConstructor);
+  CopyConstructor->setTrivial(
+    ClassDecl->needsOverloadResolutionForCopyConstructor()
+      ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor)
+      : ClassDecl->hasTrivialCopyConstructor());
 
   // C++11 [class.copy]p8:
   //   ... If the class definition does not explicitly declare a copy
@@ -9104,6 +9125,13 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
   if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor))
     CopyConstructor->setDeletedAsWritten();
 
+  // Note that we have declared this constructor.
+  ++ASTContext::NumImplicitCopyConstructorsDeclared;
+
+  if (Scope *S = getScopeForContext(ClassDecl))
+    PushOnScopeChains(CopyConstructor, S, false);
+  ClassDecl->addDecl(CopyConstructor);
+
   return CopyConstructor;
 }
 
@@ -9254,7 +9282,6 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
       Constexpr);
   MoveConstructor->setAccess(AS_public);
   MoveConstructor->setDefaulted();
-  MoveConstructor->setTrivial(ClassDecl->hasTrivialMoveConstructor());
 
   // Build an exception specification pointing back at this member.
   FunctionProtoType::ExtProtoInfo EPI;
@@ -9272,6 +9299,11 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor(
                                                SC_None, 0);
   MoveConstructor->setParams(FromParam);
 
+  MoveConstructor->setTrivial(
+    ClassDecl->needsOverloadResolutionForMoveConstructor()
+      ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor)
+      : ClassDecl->hasTrivialMoveConstructor());
+
   // C++0x [class.copy]p9:
   //   If the definition of a class X does not explicitly declare a move
   //   constructor, one will be implicitly declared as defaulted if and only if:
index 8c1058e396aac8fd30d794f30624b2e546e5becc..94f13948b56a1a6729e5cb1ffa34bc2d6467edd9 100644 (file)
@@ -1100,6 +1100,12 @@ void ASTDeclReader::ReadCXXDefinitionData(
   Data.HasOnlyCMembers = Record[Idx++];
   Data.HasInClassInitializer = Record[Idx++];
   Data.HasUninitializedReferenceMember = Record[Idx++];
+  Data.NeedOverloadResolutionForMoveConstructor = Record[Idx++];
+  Data.NeedOverloadResolutionForMoveAssignment = Record[Idx++];
+  Data.NeedOverloadResolutionForDestructor = Record[Idx++];
+  Data.DefaultedMoveConstructorIsDeleted = Record[Idx++];
+  Data.DefaultedMoveAssignmentIsDeleted = Record[Idx++];
+  Data.DefaultedDestructorIsDeleted = Record[Idx++];
   Data.HasTrivialSpecialMembers = Record[Idx++];
   Data.HasIrrelevantDestructor = Record[Idx++];
   Data.HasConstexprNonCopyMoveConstructor = Record[Idx++];
index f89ae2ef1287cfe7a39f0a1ad5ab7cba8f49f36e..ff98d9beb054fa0c966054918e7193d211b30f0c 100644 (file)
@@ -4589,6 +4589,12 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
   Record.push_back(Data.HasOnlyCMembers);
   Record.push_back(Data.HasInClassInitializer);
   Record.push_back(Data.HasUninitializedReferenceMember);
+  Record.push_back(Data.NeedOverloadResolutionForMoveConstructor);
+  Record.push_back(Data.NeedOverloadResolutionForMoveAssignment);
+  Record.push_back(Data.NeedOverloadResolutionForDestructor);
+  Record.push_back(Data.DefaultedMoveConstructorIsDeleted);
+  Record.push_back(Data.DefaultedMoveAssignmentIsDeleted);
+  Record.push_back(Data.DefaultedDestructorIsDeleted);
   Record.push_back(Data.HasTrivialSpecialMembers);
   Record.push_back(Data.HasIrrelevantDestructor);
   Record.push_back(Data.HasConstexprNonCopyMoveConstructor);
index c15e8b873c1ac9fa19132a033a2978242899d4d9..17b3191d1dc5978ea76867eee60e95a8fd76617e 100644 (file)
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
 
+// expected-no-diagnostics
+
 template<typename T, bool B> struct trivially_copyable_check {
   static_assert(B == __has_trivial_copy(T), "");
   static_assert(B == __is_trivially_constructible(T, T), "");
@@ -52,9 +54,7 @@ using _ = trivially_copyable<TemplateCtorMember>;
 struct MutableTemplateCtorMember {
   mutable TemplateCtor mtc;
 };
-// FIXME: This is wrong! The "trivial" copy constructor calls the templated
-//        constructor for the mutable member.
-static_assert(!__is_trivially_constructible(MutableTemplateCtorMember, const MutableTemplateCtorMember &), ""); // expected-error {{}}
+static_assert(!__is_trivially_constructible(MutableTemplateCtorMember, const MutableTemplateCtorMember &), "");
 static_assert(__is_trivially_constructible(MutableTemplateCtorMember, MutableTemplateCtorMember &&), "");
 struct MutableTemplateCtorMember2 {
   MutableTemplateCtorMember2(const MutableTemplateCtorMember2 &) = default;
@@ -118,3 +118,99 @@ static_assert(!__is_trivially_constructible(NCCTNT, volatile NCCTNT &), "");
 static_assert(!__is_trivially_constructible(NCCTNT, NCCTNT &&), "");
 static_assert(!__is_trivially_constructible(NCCTNT, const NCCTNT &&), "");
 static_assert(!__is_trivially_constructible(NCCTNT, volatile NCCTNT &&), "");
+
+struct TemplateCtorNoMove {
+  TemplateCtorNoMove(const TemplateCtorNoMove &) = default;
+  template<typename T> TemplateCtorNoMove(T &&);
+};
+static_assert(__is_trivially_constructible(TemplateCtorNoMove, const TemplateCtorNoMove &), "");
+static_assert(!__is_trivially_constructible(TemplateCtorNoMove, TemplateCtorNoMove &&), "");
+
+struct UseTemplateCtorNoMove {
+  TemplateCtorNoMove tcnm;
+};
+static_assert(__is_trivially_constructible(UseTemplateCtorNoMove, const UseTemplateCtorNoMove &), "");
+static_assert(!__is_trivially_constructible(UseTemplateCtorNoMove, UseTemplateCtorNoMove &&), "");
+
+struct TemplateCtorNoMoveSFINAE {
+  TemplateCtorNoMoveSFINAE(const TemplateCtorNoMoveSFINAE &) = default;
+  template<typename T, typename U = typename T::error> TemplateCtorNoMoveSFINAE(T &&);
+};
+static_assert(__is_trivially_constructible(TemplateCtorNoMoveSFINAE, const TemplateCtorNoMoveSFINAE &), "");
+static_assert(__is_trivially_constructible(TemplateCtorNoMoveSFINAE, TemplateCtorNoMoveSFINAE &&), "");
+
+struct UseTemplateCtorNoMoveSFINAE {
+  TemplateCtorNoMoveSFINAE tcnm;
+};
+static_assert(__is_trivially_constructible(UseTemplateCtorNoMoveSFINAE, const UseTemplateCtorNoMoveSFINAE &), "");
+static_assert(__is_trivially_constructible(UseTemplateCtorNoMoveSFINAE, UseTemplateCtorNoMoveSFINAE &&), "");
+
+namespace TrivialityDependsOnImplicitDeletion {
+  struct PrivateMove {
+    PrivateMove(const PrivateMove &) = default;
+  private:
+    PrivateMove(PrivateMove &&);
+    friend class Access;
+  };
+  static_assert(__is_trivially_constructible(PrivateMove, const PrivateMove &), "");
+  static_assert(!__is_trivially_constructible(PrivateMove, PrivateMove &&), "");
+
+  struct NoAccess {
+    PrivateMove pm;
+    // NoAccess's move would be deleted, so is suppressed,
+    // so moves of it use PrivateMove's copy ctor, which is trivial.
+  };
+  static_assert(__is_trivially_constructible(NoAccess, const NoAccess &), "");
+  static_assert(__is_trivially_constructible(NoAccess, NoAccess &&), "");
+  struct TopNoAccess : NoAccess {};
+  static_assert(__is_trivially_constructible(TopNoAccess, const TopNoAccess &), "");
+  static_assert(__is_trivially_constructible(TopNoAccess, TopNoAccess &&), "");
+
+  struct Access {
+    PrivateMove pm;
+    // NoAccess's move would *not* be deleted, so is *not* suppressed,
+    // so moves of it use PrivateMove's move ctor, which is not trivial.
+  };
+  static_assert(__is_trivially_constructible(Access, const Access &), "");
+  static_assert(!__is_trivially_constructible(Access, Access &&), "");
+  struct TopAccess : Access {};
+  static_assert(__is_trivially_constructible(TopAccess, const TopAccess &), "");
+  static_assert(!__is_trivially_constructible(TopAccess, TopAccess &&), "");
+}
+
+namespace TrivialityDependsOnDestructor {
+  class HasInaccessibleDestructor { ~HasInaccessibleDestructor() = default; };
+  struct HasImplicitlyDeletedDestructor : HasInaccessibleDestructor {};
+  struct HasImplicitlyDeletedCopyCtor : HasImplicitlyDeletedDestructor {
+    HasImplicitlyDeletedCopyCtor() = default;
+    template<typename T> HasImplicitlyDeletedCopyCtor(T &&);
+    // Copy ctor is deleted but trivial.
+    // Move ctor is suppressed.
+    HasImplicitlyDeletedCopyCtor(const HasImplicitlyDeletedCopyCtor&) = default;
+    HasImplicitlyDeletedCopyCtor(HasImplicitlyDeletedCopyCtor&&) = default;
+  };
+  struct Test : HasImplicitlyDeletedCopyCtor {
+    Test(const Test&) = default;
+    Test(Test&&) = default;
+  };
+  // Implicit copy ctor calls deleted trivial copy ctor.
+  static_assert(__has_trivial_copy(Test), "");
+  // This is false because the destructor is deleted.
+  static_assert(!__is_trivially_constructible(Test, const Test &), "");
+  // Implicit move ctor calls template ctor.
+  static_assert(!__is_trivially_constructible(Test, Test &&), "");
+
+  struct HasAccessibleDestructor { ~HasAccessibleDestructor() = default; };
+  struct HasImplicitlyDefaultedDestructor : HasAccessibleDestructor {};
+  struct HasImplicitlyDefaultedCopyCtor : HasImplicitlyDefaultedDestructor {
+    template<typename T> HasImplicitlyDefaultedCopyCtor(T &&);
+    // Copy ctor is trivial.
+    // Move ctor is trivial.
+  };
+  struct Test2 : HasImplicitlyDefaultedCopyCtor {};
+  // Implicit copy ctor calls trivial copy ctor.
+  static_assert(__has_trivial_copy(Test2), "");
+  static_assert(__is_trivially_constructible(Test2, const Test2 &), "");
+  // Implicit move ctor calls trivial move ctor.
+  static_assert(__is_trivially_constructible(Test2, Test2 &&), "");
+}
index e9df3b3f394df6ae7f85e5bc8c6f234aff959566..c7224aea95909b36e57e59569a62d2451805de36 100644 (file)
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
 
+// expected-no-diagnostics
+
 template<typename T, bool B> struct trivially_assignable_check {
   static_assert(B == __has_trivial_assign(T), "");
   static_assert(B == __is_trivially_assignable(T&, T), "");
@@ -53,9 +55,7 @@ using _ = trivially_assignable<TemplateCtorMember>;
 struct MutableTemplateCtorMember {
   mutable TemplateCtor mtc;
 };
-// FIXME: This is wrong! The "trivial" copy constructor calls the templated
-//        constructor for the mutable member.
-static_assert(!__is_trivially_assignable(MutableTemplateCtorMember, const MutableTemplateCtorMember &), ""); // expected-error {{}}
+static_assert(!__is_trivially_assignable(MutableTemplateCtorMember, const MutableTemplateCtorMember &), "");
 static_assert(__is_trivially_assignable(MutableTemplateCtorMember, MutableTemplateCtorMember &&), "");
 
 // Both trivial and non-trivial special members.
@@ -141,3 +141,62 @@ static_assert(__is_trivially_assignable(DerivedFromRefQualifier&, const DerivedF
 static_assert(__is_trivially_assignable(DerivedFromRefQualifier&&, const DerivedFromRefQualifier&), "");
 static_assert(!__is_trivially_assignable(DerivedFromRefQualifier&, DerivedFromRefQualifier&&), "");
 static_assert(!__is_trivially_assignable(DerivedFromRefQualifier&&, DerivedFromRefQualifier&&), "");
+
+struct TemplateAssignNoMove {
+  TemplateAssignNoMove &operator=(const TemplateAssignNoMove &) = default;
+  template<typename T> TemplateAssignNoMove &operator=(T &&);
+};
+static_assert(__is_trivially_assignable(TemplateAssignNoMove, const TemplateAssignNoMove &), "");
+static_assert(!__is_trivially_assignable(TemplateAssignNoMove, TemplateAssignNoMove &&), "");
+
+struct UseTemplateAssignNoMove {
+  TemplateAssignNoMove tanm;
+};
+static_assert(__is_trivially_assignable(UseTemplateAssignNoMove, const UseTemplateAssignNoMove &), "");
+static_assert(!__is_trivially_assignable(UseTemplateAssignNoMove, UseTemplateAssignNoMove &&), "");
+
+struct TemplateAssignNoMoveSFINAE {
+  TemplateAssignNoMoveSFINAE &operator=(const TemplateAssignNoMoveSFINAE &) = default;
+  template<typename T, typename U = typename T::error> TemplateAssignNoMoveSFINAE &operator=(T &&);
+};
+static_assert(__is_trivially_assignable(TemplateAssignNoMoveSFINAE, const TemplateAssignNoMoveSFINAE &), "");
+static_assert(__is_trivially_assignable(TemplateAssignNoMoveSFINAE, TemplateAssignNoMoveSFINAE &&), "");
+
+struct UseTemplateAssignNoMoveSFINAE {
+  TemplateAssignNoMoveSFINAE tanm;
+};
+static_assert(__is_trivially_assignable(UseTemplateAssignNoMoveSFINAE, const UseTemplateAssignNoMoveSFINAE &), "");
+static_assert(__is_trivially_assignable(UseTemplateAssignNoMoveSFINAE, UseTemplateAssignNoMoveSFINAE &&), "");
+
+namespace TrivialityDependsOnImplicitDeletion {
+  struct PrivateMove {
+    PrivateMove &operator=(const PrivateMove &) = default;
+  private:
+    PrivateMove &operator=(PrivateMove &&);
+    friend class Access;
+  };
+  static_assert(__is_trivially_assignable(PrivateMove, const PrivateMove &), "");
+  static_assert(!__is_trivially_assignable(PrivateMove, PrivateMove &&), "");
+
+  struct NoAccess {
+    PrivateMove pm;
+    // NoAccess's move would be deleted, so is suppressed,
+    // so moves of it use PrivateMove's copy ctor, which is trivial.
+  };
+  static_assert(__is_trivially_assignable(NoAccess, const NoAccess &), "");
+  static_assert(__is_trivially_assignable(NoAccess, NoAccess &&), "");
+  struct TopNoAccess : NoAccess {};
+  static_assert(__is_trivially_assignable(TopNoAccess, const TopNoAccess &), "");
+  static_assert(__is_trivially_assignable(TopNoAccess, TopNoAccess &&), "");
+
+  struct Access {
+    PrivateMove pm;
+    // NoAccess's move would *not* be deleted, so is *not* suppressed,
+    // so moves of it use PrivateMove's move ctor, which is not trivial.
+  };
+  static_assert(__is_trivially_assignable(Access, const Access &), "");
+  static_assert(!__is_trivially_assignable(Access, Access &&), "");
+  struct TopAccess : Access {};
+  static_assert(__is_trivially_assignable(TopAccess, const TopAccess &), "");
+  static_assert(!__is_trivially_assignable(TopAccess, TopAccess &&), "");
+}
index 39a90b858fd21df7f7660e53db114151f79c4395..230f6a61471569521fd7a6bf63763ede316d03da 100644 (file)
@@ -20,6 +20,11 @@ class quux {
   ~quux() = default;
 };
 
+struct A {
+  A(const A&) = default;
+  template<typename T> A(T&&);
+};
+
 #else
 
 foo::foo() { } // expected-error{{definition of explicitly defaulted default constructor}}
@@ -31,4 +36,11 @@ void fn() {
 baz bz; // expected-error{{deleted function}} expected-note@16{{deleted here}}
 quux qx; // expected-error{{private destructor}} expected-note@20{{private here}}
 
+struct B { A a; };
+struct C { mutable A a; };
+static_assert(__is_trivially_constructible(B, const B&), "");
+static_assert(!__is_trivially_constructible(B, B&&), "");
+static_assert(!__is_trivially_constructible(C, const C&), "");
+static_assert(!__is_trivially_constructible(C, C&&), "");
+
 #endif
index 54294bcbb8b19e6041a92541198e46ac3ab54f5d..4b6c44d2578f5e5d2b15ff11b74e354987048482 100644 (file)
@@ -1224,6 +1224,7 @@ void has_trivial_copy_constructor() {
   { int arr[T(__has_trivial_copy(AllDefaulted))]; }
   { int arr[T(__has_trivial_copy(AllDeleted))]; }
   { int arr[T(__has_trivial_copy(DerivesAr))]; }
+  { int arr[T(__has_trivial_copy(DerivesHasRef))]; }
 
   { int arr[F(__has_trivial_copy(HasCopy))]; }
   { int arr[F(__has_trivial_copy(HasTemplateCons))]; }
@@ -1251,6 +1252,7 @@ void has_trivial_copy_assignment() {
   { int arr[T(__has_trivial_assign(AllDefaulted))]; }
   { int arr[T(__has_trivial_assign(AllDeleted))]; }
   { int arr[T(__has_trivial_assign(DerivesAr))]; }
+  { int arr[T(__has_trivial_assign(DerivesHasRef))]; }
 
   { int arr[F(__has_trivial_assign(IntRef))]; }
   { int arr[F(__has_trivial_assign(HasCopyAssign))]; }
@@ -1286,6 +1288,7 @@ void has_trivial_destructor() {
   { int arr[T(__has_trivial_destructor(VirtAr))]; }
   { int arr[T(__has_trivial_destructor(AllDefaulted))]; }
   { int arr[T(__has_trivial_destructor(AllDeleted))]; }
+  { int arr[T(__has_trivial_destructor(DerivesHasRef))]; }
 
   { int arr[F(__has_trivial_destructor(HasDest))]; }
   { int arr[F(__has_trivial_destructor(void))]; }