llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Barry Revzin (brevzin)

<details>
<summary>Changes</summary>

This commit makes union construction/destruction trivial by default, but does 
not add the lifetime aspects of the paper — am holding out until P3726R0 will 
be discussed which alters that approach somewhat based on feedback from Richard 
Smith. But the constructor/destructor part is common regardless and also 
simpler.

---
Full diff: https://github.com/llvm/llvm-project/pull/146815.diff


6 Files Affected:

- (modified) clang/lib/AST/DeclCXX.cpp (+6-3) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+15-1) 
- (modified) clang/test/CXX/drs/cwg6xx.cpp (+2-2) 
- (modified) clang/test/CXX/special/class.ctor/p5-0x.cpp (+8-7) 
- (modified) clang/test/CXX/special/class.ctor/p6-0x.cpp (+15-13) 
- (modified) clang/test/CXX/special/class.dtor/p5-0x.cpp (+17-16) 


``````````diff
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index ccb308e103253..49adbdf1c393d 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1217,6 +1217,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
     // those because they are always unnamed.
     bool IsZeroSize = Field->isZeroSize(Context);
 
+    // P3074
+    const bool TrivialUnion = Context.getLangOpts().CPlusPlus26 && isUnion();
+
     if (const auto *RecordTy = T->getAs<RecordType>()) {
       auto *FieldRec = cast<CXXRecordDecl>(RecordTy->getDecl());
       if (FieldRec->getDefinition()) {
@@ -1277,7 +1280,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
         //    -- for all the non-static data members of its class that are of
         //       class type (or array thereof), each such class has a trivial
         //       default constructor.
-        if (!FieldRec->hasTrivialDefaultConstructor())
+        if (!FieldRec->hasTrivialDefaultConstructor() && !TrivialUnion)
           data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
 
         // C++0x [class.copy]p13:
@@ -1315,9 +1318,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
         if (!FieldRec->hasTrivialMoveAssignment())
           data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
 
-        if (!FieldRec->hasTrivialDestructor())
+        if (!FieldRec->hasTrivialDestructor() && !TrivialUnion)
           data().HasTrivialSpecialMembers &= ~SMF_Destructor;
-        if (!FieldRec->hasTrivialDestructorForCall())
+        if (!FieldRec->hasTrivialDestructorForCall() && !TrivialUnion)
           data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
         if (!FieldRec->hasIrrelevantDestructor())
           data().HasIrrelevantDestructor = false;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e8c65025bfe6d..e9b45bf99620c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9511,6 +9511,15 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
   CXXMethodDecl *Decl = SMOR.getMethod();
   FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>();
 
+  // P3074: default ctor and dtor for unions are not deleted, regardless of
+  // whether the underlying fields have non-trivial or deleted versions of 
those
+  // members
+  if (S.Context.getLangOpts().CPlusPlus26)
+    if (Field && Field->getParent()->isUnion() &&
+        (CSM == CXXSpecialMemberKind::DefaultConstructor ||
+         CSM == CXXSpecialMemberKind::Destructor))
+      return false;
+
   int DiagKind = -1;
 
   if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted)
@@ -9774,7 +9783,8 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
 
       // At least one member in each anonymous union must be non-const
       if (CSM == CXXSpecialMemberKind::DefaultConstructor &&
-          AllVariantFieldsAreConst && !FieldRecord->field_empty()) {
+          AllVariantFieldsAreConst && !FieldRecord->field_empty() &&
+          !this->S.Context.getLangOpts().CPlusPlus26) {
         if (Diagnose)
           S.Diag(FieldRecord->getLocation(),
                  diag::note_deleted_default_ctor_all_const)
@@ -9804,6 +9814,10 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
   // default constructor. Don't do that.
   if (CSM == CXXSpecialMemberKind::DefaultConstructor && inUnion() &&
       AllFieldsAreConst) {
+
+    if (S.Context.getLangOpts().CPlusPlus26)
+      return false;
+
     bool AnyFields = false;
     for (auto *F : MD->getParent()->fields())
       if ((AnyFields = !F->isUnnamedBitField()))
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index e2eb009508b52..dd54bb5295b56 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -4,7 +4,7 @@
 // RUN: %clang_cc1 -std=c++17 %s 
-verify=expected,cxx11-20,cxx98-17,cxx11-17,since-cxx11 -fexceptions 
-fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx11-20,since-cxx11 
-fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx11 -fexceptions 
-fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx11 -fexceptions 
-fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx11,cxx26 
-fexceptions -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus == 199711L
 #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -922,7 +922,7 @@ namespace cwg667 { // cwg667: 8
 
   struct B { ~B() = delete; };
   union C { B b; };
-  static_assert(!__is_trivially_destructible(C), "");
+  static_assert(!__is_trivially_destructible(C), ""); // cxx26-error {{failed}}
 
   struct D { D(const D&) = delete; };
   struct E : D {};
diff --git a/clang/test/CXX/special/class.ctor/p5-0x.cpp 
b/clang/test/CXX/special/class.ctor/p5-0x.cpp
index e0c53058f892b..97a8b035a0705 100644
--- a/clang/test/CXX/special/class.ctor/p5-0x.cpp
+++ b/clang/test/CXX/special/class.ctor/p5-0x.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,until26 %s -std=c++11 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -fsyntax-only -verify=expected %s -std=c++26 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
 
 struct DefaultedDefCtor1 {};
 struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; };
@@ -23,8 +24,8 @@ int n;
 
 // - X is a union-like class that has a variant member with a non-trivial
 // default constructor,
-union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{default 
constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has 
a non-trivial default constructor}}
-Deleted1a d1a; // expected-error {{implicitly-deleted default constructor}}
+union Deleted1a { UserProvidedDefCtor u; }; // until26-note {{default 
constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has 
a non-trivial default constructor}}
+Deleted1a d1a; // until26-error {{implicitly-deleted default constructor}}
 union NotDeleted1a { DefaultedDefCtor1 nu; };
 NotDeleted1a nd1a;
 union NotDeleted1b { DefaultedDefCtor2 nu; };
@@ -86,19 +87,19 @@ NotDeleted3i nd3i;
 union Deleted4a {
   const int a;
   const int b;
-  const UserProvidedDefCtor c; // expected-note {{because variant field 'c' 
has a non-trivial default constructor}}
+  const UserProvidedDefCtor c; // until26-note {{because variant field 'c' has 
a non-trivial default constructor}}
 };
-Deleted4a d4a; // expected-error {{implicitly-deleted default constructor}}
+Deleted4a d4a; // until26-error {{implicitly-deleted default constructor}}
 union NotDeleted4a { const int a; int b; };
 NotDeleted4a nd4a;
 
 // - X is a non-union class and all members of any anonymous union member are 
of
 // const-qualified type (or array thereof),
 struct Deleted5a {
-  union { const int a; }; // expected-note {{because all data members of an 
anonymous union member are const-qualified}}
+  union { const int a; }; // until26-note {{because all data members of an 
anonymous union member are const-qualified}}
   union { int b; };
 };
-Deleted5a d5a; // expected-error {{implicitly-deleted default constructor}}
+Deleted5a d5a; // until26-error {{implicitly-deleted default constructor}}
 struct NotDeleted5a { union { const int a; int b; }; union { const int c; int 
d; }; };
 NotDeleted5a nd5a;
 
diff --git a/clang/test/CXX/special/class.ctor/p6-0x.cpp 
b/clang/test/CXX/special/class.ctor/p6-0x.cpp
index 156a2b20c6b52..6ce1b06841ab4 100644
--- a/clang/test/CXX/special/class.ctor/p6-0x.cpp
+++ b/clang/test/CXX/special/class.ctor/p6-0x.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,until26 %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx26 %s -std=c++26
 
 // Implicitly-defined default constructors are constexpr if the implicit
 // definition would be.
@@ -15,8 +16,9 @@ constexpr NonConstexpr2a nc2a = NonConstexpr2a(); // ok, does 
not call construct
 constexpr int nc2_a = NonConstexpr2().nl.a; // ok
 constexpr int nc2a_a = NonConstexpr2a().a; // ok
 struct Helper {
-  friend constexpr NonConstexpr1::NonConstexpr1(); // expected-error {{follows 
non-constexpr declaration}}
-  friend constexpr NonConstexpr2::NonConstexpr2(); // expected-error {{follows 
non-constexpr declaration}}
+  friend constexpr NonConstexpr1::NonConstexpr1(); // until26-error {{follows 
non-constexpr declaration}} cxx26-error {{missing exception specification}}
+  friend constexpr NonConstexpr2::NonConstexpr2(); // until26-error {{follows 
non-constexpr declaration}} cxx26-error {{missing exception specification}}
+
 };
 
 struct Constexpr1 {};
@@ -31,14 +33,14 @@ constexpr Constexpr2 c2 = Constexpr2(); // ok
 
 int n;
 struct Member {
-  Member() : a(n) {}
+  Member() : a(n) {} // cxx26-note {{here}}
   constexpr Member(int&a) : a(a) {}
   int &a;
 };
-struct NonConstexpr4 { // expected-note {{here}}
+struct NonConstexpr4 { // until26-note {{here}} cxx26-note {{non-constexpr 
constructor}}
   Member m;
 };
-constexpr NonConstexpr4 nc4 = NonConstexpr4(); // expected-error {{constant 
expression}} expected-note {{non-constexpr constructor 'NonConstexpr4'}}
+constexpr NonConstexpr4 nc4 = NonConstexpr4(); // expected-error {{constant 
expression}} until26-note {{non-constexpr constructor 'NonConstexpr4'}} 
cxx26-note {{in call to}}
 struct Constexpr3 {
   constexpr Constexpr3() : m(n) {}
   Member m;
@@ -53,11 +55,11 @@ constexpr Constexpr4 c4 = Constexpr4(); // ok
 // This rule breaks some legal C++98 programs!
 struct A {}; // expected-note {{here}}
 struct B {
-  friend A::A(); // expected-error {{non-constexpr declaration of 'A' follows 
constexpr declaration}}
+  friend A::A(); // until26-error {{non-constexpr declaration of 'A' follows 
constexpr declaration}} cxx26-error {{missing exception specification}}
 };
 
 namespace UnionCtors {
-  union A { // expected-note {{here}}
+  union A { // until26-note {{here}}
     int a;
     int b;
   };
@@ -79,7 +81,7 @@ namespace UnionCtors {
       int d = 5;
     };
   };
-  struct E { // expected-note {{here}}
+  struct E { // until26-note {{here}}
     union {
       int a;
       int b;
@@ -87,11 +89,11 @@ namespace UnionCtors {
   };
 
   struct Test {
-    friend constexpr A::A() noexcept; // expected-error {{follows 
non-constexpr declaration}}
+    friend constexpr A::A() noexcept; // until26-error {{follows non-constexpr 
declaration}}
     friend constexpr B::B() noexcept;
     friend constexpr C::C() noexcept;
     friend constexpr D::D() noexcept;
-    friend constexpr E::E() noexcept; // expected-error {{follows 
non-constexpr declaration}}
+    friend constexpr E::E() noexcept; // until26-error {{follows non-constexpr 
declaration}}
   };
 }
 
@@ -122,6 +124,6 @@ namespace PR48763 {
 
   struct G { G(); };
   struct H : D { using D::D; H(int); G g; };
-  union V { H h; }; // expected-note {{field 'h' has a non-trivial default 
constructor}}
-  V v; // expected-error {{deleted}}
+  union V { H h; }; // until26-note {{field 'h' has a non-trivial default 
constructor}}
+  V v; // until26-error {{deleted}}
 }
diff --git a/clang/test/CXX/special/class.dtor/p5-0x.cpp 
b/clang/test/CXX/special/class.dtor/p5-0x.cpp
index ae14dcdaf102a..7616383515e0a 100644
--- a/clang/test/CXX/special/class.dtor/p5-0x.cpp
+++ b/clang/test/CXX/special/class.dtor/p5-0x.cpp
@@ -1,10 +1,11 @@
-// RUN: %clang_cc1 -verify -std=c++11 %s -Wno-defaulted-function-deleted 
-triple x86_64-linux-gnu
+// RUN: %clang_cc1 -verify=expected,until26 -std=c++11 %s 
-Wno-defaulted-function-deleted -triple x86_64-linux-gnu
+// RUN: %clang_cc1 -verify=expected -std=c++26 %s 
-Wno-defaulted-function-deleted -triple x86_64-linux-gnu
 
 struct NonTrivDtor {
   ~NonTrivDtor();
 };
 struct DeletedDtor {
-  ~DeletedDtor() = delete; // expected-note 5 {{deleted here}}
+  ~DeletedDtor() = delete; // expected-note 4+ {{deleted here}}
 };
 class InaccessibleDtor {
   ~InaccessibleDtor() = default;
@@ -16,28 +17,28 @@ class InaccessibleDtor {
 // destructor.
 union A1 {
   A1();
-  NonTrivDtor n; // expected-note {{destructor of 'A1' is implicitly deleted 
because variant field 'n' has a non-trivial destructor}}
+  NonTrivDtor n; // until26-note {{destructor of 'A1' is implicitly deleted 
because variant field 'n' has a non-trivial destructor}}
 };
-A1 a1; // expected-error {{deleted function}}
+A1 a1; // until26-error {{deleted function}}
 struct A2 {
   A2();
   union {
-    NonTrivDtor n; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+    NonTrivDtor n; // until26-note {{because variant field 'n' has a 
non-trivial destructor}}
   };
 };
-A2 a2; // expected-error {{deleted function}}
+A2 a2; // until26-error {{deleted function}}
 union A3 {
   A3();
-  NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+  NonTrivDtor n[3]; // until26-note {{because variant field 'n' has a 
non-trivial destructor}}
 };
-A3 a3; // expected-error {{deleted function}}
+A3 a3; // until26-error {{deleted function}}
 struct A4 {
   A4();
   union {
-    NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+    NonTrivDtor n[3]; // until26-note {{because variant field 'n' has a 
non-trivial destructor}}
   };
 };
-A4 a4; // expected-error {{deleted function}}
+A4 a4; // until26-error {{deleted function}}
 
 // -- any of the non-static data members has class type M (or array thereof) 
and
 // M has a deleted or inaccessible destructor.
@@ -63,18 +64,18 @@ struct B4 {
 B4 b4; // expected-error {{deleted function}}
 union B5 {
   B5();
-  union { // expected-note-re {{because field 'B5::(anonymous union at 
{{.+}})' has a deleted destructor}}
-    DeletedDtor a; // expected-note {{because field 'a' has a deleted 
destructor}}
+  union { // until26-note-re {{because field 'B5::(anonymous union at {{.+}})' 
has a deleted destructor}}
+    DeletedDtor a; // until26-note {{because field 'a' has a deleted 
destructor}}
   };
 };
-B5 b5; // expected-error {{deleted function}}
+B5 b5; // until26-error {{deleted function}}
 union B6 {
   B6();
-  union { // expected-note-re {{because field 'B6::(anonymous union at 
{{.+}})' has a deleted destructor}}
-    InaccessibleDtor a; // expected-note {{because field 'a' has an 
inaccessible destructor}}
+  union { // until26-note-re {{because field 'B6::(anonymous union at {{.+}})' 
has a deleted destructor}}
+    InaccessibleDtor a; // until26-note {{because field 'a' has an 
inaccessible destructor}}
   };
 };
-B6 b6; // expected-error {{deleted function}}
+B6 b6; // until26-error {{deleted function}}
 
 // -- any direct or virtual base class has a deleted or inaccessible 
destructor.
 struct C1 : DeletedDtor { C1(); } c1; // expected-error {{deleted function}} 
expected-note {{base class 'DeletedDtor' has a deleted destructor}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/146815
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to