[clang] Sema: Fix CXXRecordDecl::isTriviallyCopyable() for classes with all deleted special functions. (PR #94831)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 7 20:14:28 PDT 2024


https://github.com/pcc created https://github.com/llvm/llvm-project/pull/94831

C++17 added the following requirement for a class to be trivially
copyable: "that has at least one non-deleted copy constructor,
move constructor, copy assignment operator, or move assignment
operator". However, this was not implemented. Fix it.


>From f725de0cd89b0dd90ce5209cb6b3d9632a52f3c9 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <pcc at google.com>
Date: Fri, 7 Jun 2024 20:14:15 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.6-beta.1
---
 .../clang/AST/CXXRecordDeclDefinitionBits.def |  3 +++
 clang/lib/AST/DeclCXX.cpp                     | 25 ++++++++++++++++---
 clang/test/SemaCXX/type-traits.cpp            |  8 ++++++
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index cdf0804680ad0..55cd45348d29d 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -249,4 +249,7 @@ FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR)
 /// base classes or fields have a no-return destructor
 FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
 
+/// The special members of this class which were deleted.
+FIELD(HasDeletedSpecialMembers, 6, MERGE_OR)
+
 #undef FIELD
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 7f2c786547b9b..a3de4d3654006 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -109,9 +109,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
       ImplicitCopyAssignmentHasConstParam(true),
       HasDeclaredCopyConstructorWithConstParam(false),
       HasDeclaredCopyAssignmentWithConstParam(false),
-      IsAnyDestructorNoReturn(false), IsLambda(false),
-      IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
-      HasODRHash(false), Definition(D) {}
+      IsAnyDestructorNoReturn(false), HasDeletedSpecialMembers(0),
+      IsLambda(false), IsParsingBaseSpecifiers(false),
+      ComputedVisibleConversions(false), HasODRHash(false), Definition(D) {}
 
 CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
   return Bases.get(Definition->getASTContext().getExternalSource());
@@ -586,6 +586,14 @@ bool CXXRecordDecl::isTriviallyCopyable() const {
   if (hasNonTrivialMoveAssignment()) return false;
   //   -- has a trivial destructor.
   if (!hasTrivialDestructor()) return false;
+  // C++17 [class]p6: that has at least one non-deleted copy constructor, move
+  // constructor, copy assignment operator, or move assignment operator,
+  if (getASTContext().getLangOpts().CPlusPlus17 &&
+      (data().HasDeletedSpecialMembers &
+       (SMF_CopyAssignment | SMF_CopyConstructor | SMF_MoveAssignment |
+        SMF_MoveConstructor)) == (SMF_CopyAssignment | SMF_CopyConstructor |
+                                  SMF_MoveAssignment | SMF_MoveConstructor))
+    return false;
 
   return true;
 }
@@ -1446,7 +1454,10 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
     // out whether it's trivial yet (not until we get to the end of the
     // class). We'll handle this method in
     // finishedDefaultedOrDeletedMember.
-  } else if (MD->isTrivial()) {
+    return;
+  }
+
+  if (MD->isTrivial()) {
     data().HasTrivialSpecialMembers |= SMKind;
     data().HasTrivialSpecialMembersForCall |= SMKind;
   } else if (MD->isTrivialForCall()) {
@@ -1462,6 +1473,10 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
     if (!MD->isUserProvided())
       data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
   }
+
+  if (MD->isDeleted()) {
+    data().HasDeletedSpecialMembers |= SMKind;
+  }
 }
 
 void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
@@ -1499,6 +1514,8 @@ void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
       data().HasTrivialSpecialMembers |= SMKind;
     else
       data().DeclaredNonTrivialSpecialMembers |= SMKind;
+    if (D->isDeleted())
+      data().HasDeletedSpecialMembers |= SMKind;
   }
 }
 
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index d40605f56f1ed..27b0d204d5690 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -1377,7 +1377,11 @@ void is_trivial2()
   static_assert(__is_trivial(UnionAr));
   static_assert(__is_trivial(TrivialStruct));
   static_assert(__is_trivial(AllDefaulted));
+#if __cplusplus >= 201703L
+  static_assert(!__is_trivial(AllDeleted));
+#else
   static_assert(__is_trivial(AllDeleted));
+#endif
 
   static_assert(!__is_trivial(void));
   static_assert(!__is_trivial(NonTrivialStruct));
@@ -1419,7 +1423,11 @@ void is_trivially_copyable2()
   static_assert(__is_trivially_copyable(TrivialStruct));
   static_assert(__is_trivially_copyable(NonTrivialStruct));
   static_assert(__is_trivially_copyable(AllDefaulted));
+#if __cplusplus >= 201703L
+  static_assert(!__is_trivially_copyable(AllDeleted));
+#else
   static_assert(__is_trivially_copyable(AllDeleted));
+#endif
 
   static_assert(!__is_trivially_copyable(void));
   static_assert(!__is_trivially_copyable(SuperNonTrivialStruct));



More information about the cfe-commits mailing list