https://github.com/brevzin updated https://github.com/llvm/llvm-project/pull/146815
>From 40290a957b6f349a9b670193c8bc699d8eb7d373 Mon Sep 17 00:00:00 2001 From: Barry Revzin <barry.rev...@gmail.com> Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/5] [P3074] Implementing part of trivial unions --- clang/lib/AST/DeclCXX.cpp | 9 ++++-- clang/lib/Sema/SemaDeclCXX.cpp | 16 +++++++++- clang/test/CXX/drs/cwg6xx.cpp | 4 +-- clang/test/CXX/special/class.ctor/p5-0x.cpp | 15 +++++----- clang/test/CXX/special/class.ctor/p6-0x.cpp | 28 +++++++++-------- clang/test/CXX/special/class.dtor/p5-0x.cpp | 33 +++++++++++---------- 6 files changed, 63 insertions(+), 42 deletions(-) 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..225409a69df53 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() && + !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}} >From 59b0649e184ab1ea73b3b239ae0240bb2dcd3ac4 Mon Sep 17 00:00:00 2001 From: Barry Revzin <barry.rev...@gmail.com> Date: Wed, 2 Jul 2025 23:04:59 -0500 Subject: [PATCH 2/5] Adding test case that I have to fix still --- clang/test/CXX/special/class.dtor/p7.cpp | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 clang/test/CXX/special/class.dtor/p7.cpp diff --git a/clang/test/CXX/special/class.dtor/p7.cpp b/clang/test/CXX/special/class.dtor/p7.cpp new file mode 100644 index 0000000000000..b9b2e2bde5676 --- /dev/null +++ b/clang/test/CXX/special/class.dtor/p7.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -verify -std=c++26 %s -Wno-defaulted-function-deleted -triple x86_64-linux-gnu + +struct NonTrivial { + NonTrivial(int) { } + ~NonTrivial() { } +}; + +union U0 { + NonTrivial nt; + int i; + }; + U0 u0; + +// overload resolution to select a constructor to default-initialize an object of type X either fails +union U1 { + U1(int); + NonTrivial nt; +}; +U1 u1(1); // expected-error {{deleted destructor}} + +// or selects a constructor that is either deleted or not trivial, or +union U2 { + U2() : nt(2) { } + NonTrivial nt; +}; +U2 u2; // expected-error {{deleted destructor}} + +union U3 { + U3() = delete; + U3(int); + NonTrivial nt; +}; +U3 u3(1); // expected-error {{deleted destructor}} + +// or X has a variant member V of class type M (or possibly multi-dimensional array thereof) where V has a default member initializer and M has a destructor that is non-trivial, +union U4 { + NonTrivial nt = 1; +}; +U4 u4; // expected-error {{deleted destructor}} + +union U5 { + NonTrivial nt; + U5* next = nullptr; +}; +U5 u5; + + + >From e7cc4ec355367a06a85e154cc616a647616fe451 Mon Sep 17 00:00:00 2001 From: Barry Revzin <barry.rev...@gmail.com> Date: Wed, 2 Jul 2025 23:27:25 -0500 Subject: [PATCH 3/5] One down --- clang/lib/Sema/SemaDeclCXX.cpp | 5 +++++ clang/test/CXX/special/class.dtor/p7.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 225409a69df53..0a7e71cfcf0fe 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9705,6 +9705,11 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD)) return true; + if (S.Context.getLangOpts().CPlusPlus26 && FD->hasInClassInitializer() && + FieldRecord && !FieldRecord->hasTrivialDestructor() && + CSM == CXXSpecialMemberKind::Destructor) + return true; + if (CSM == CXXSpecialMemberKind::DefaultConstructor) { // For a default constructor, all references must be initialized in-class // and, if a union, it must have a non-const member. diff --git a/clang/test/CXX/special/class.dtor/p7.cpp b/clang/test/CXX/special/class.dtor/p7.cpp index b9b2e2bde5676..1b50bdc3920e0 100644 --- a/clang/test/CXX/special/class.dtor/p7.cpp +++ b/clang/test/CXX/special/class.dtor/p7.cpp @@ -16,27 +16,27 @@ union U1 { U1(int); NonTrivial nt; }; -U1 u1(1); // expected-error {{deleted destructor}} +U1 u1(1); // expected-error {{deleted function}} // or selects a constructor that is either deleted or not trivial, or union U2 { U2() : nt(2) { } NonTrivial nt; }; -U2 u2; // expected-error {{deleted destructor}} +U2 u2; // expected-error {{deleted function}} union U3 { U3() = delete; U3(int); NonTrivial nt; }; -U3 u3(1); // expected-error {{deleted destructor}} +U3 u3(1); // expected-error {{deleted function}} // or X has a variant member V of class type M (or possibly multi-dimensional array thereof) where V has a default member initializer and M has a destructor that is non-trivial, union U4 { NonTrivial nt = 1; }; -U4 u4; // expected-error {{deleted destructor}} +U4 u4; // expected-error {{deleted function}} union U5 { NonTrivial nt; >From 5eb68c59001ccb8fbe502b07b14dc70749c931da Mon Sep 17 00:00:00 2001 From: Barry Revzin <barry.rev...@gmail.com> Date: Thu, 3 Jul 2025 10:26:30 -0500 Subject: [PATCH 4/5] More accurate --- clang/lib/Sema/SemaDeclCXX.cpp | 45 +++++++++++++++------ clang/test/CXX/special/class.dtor/p5-0x.cpp | 28 ++++++------- clang/test/CXX/special/class.dtor/p7.cpp | 43 +++++++++++--------- 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 0a7e71cfcf0fe..089ffa1d2dea2 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9511,15 +9511,6 @@ 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) @@ -9552,6 +9543,35 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( if (DiagKind == -1) return false; + if (this->S.Context.getLangOpts().CPlusPlus26 && inUnion() && + CSM == CXXSpecialMemberKind::Destructor) { + // CXXRecordDecl *FieldRecord = Subobj.dyn_cast<CXXRecordDecl*>(); + // [class.dtor]/7 In C++26, a destructor for a union X is only deleted under + // the additional conditions that: + + // overload resolution to select a constructor to default-initialize an + // object of type X either fails or selects a constructor that is either + // deleted or not trivial, or + // or X has a variant member V of class type M (or possibly + // multi-dimensional array thereof) where V has a default member initializer + // and M has a destructor that is non-trivial, + + Sema::SpecialMemberOverloadResult SMOR = + S.LookupSpecialMember(dyn_cast<CXXRecordDecl>(Field->getParent()), + CXXSpecialMemberKind::DefaultConstructor, false, + false, false, false, false); + if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Success) { + CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(SMOR.getMethod()); + if (Ctor->isTrivial()) { + return false; + } + + if (!Ctor->isUserProvided() && !Field->hasInClassInitializer()) { + return false; + } + } + } + if (Diagnose) { if (Field) { S.Diag(Field->getLocation(), @@ -9705,10 +9725,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD)) return true; - if (S.Context.getLangOpts().CPlusPlus26 && FD->hasInClassInitializer() && - FieldRecord && !FieldRecord->hasTrivialDestructor() && - CSM == CXXSpecialMemberKind::Destructor) - return true; + if (inUnion() && S.Context.getLangOpts().CPlusPlus26 && + CSM == CXXSpecialMemberKind::DefaultConstructor) + return false; if (CSM == CXXSpecialMemberKind::DefaultConstructor) { // For a default constructor, all references must be initialized in-class diff --git a/clang/test/CXX/special/class.dtor/p5-0x.cpp b/clang/test/CXX/special/class.dtor/p5-0x.cpp index 7616383515e0a..b8ebb41b24ed7 100644 --- a/clang/test/CXX/special/class.dtor/p5-0x.cpp +++ b/clang/test/CXX/special/class.dtor/p5-0x.cpp @@ -17,28 +17,28 @@ class InaccessibleDtor { // destructor. union A1 { A1(); - NonTrivDtor n; // until26-note {{destructor of 'A1' is implicitly deleted because variant field 'n' has a non-trivial destructor}} + NonTrivDtor n; // expected-note {{destructor of 'A1' is implicitly deleted because variant field 'n' has a non-trivial destructor}} }; -A1 a1; // until26-error {{deleted function}} +A1 a1; // expected-error {{deleted function}} struct A2 { A2(); union { - NonTrivDtor n; // until26-note {{because variant field 'n' has a non-trivial destructor}} + NonTrivDtor n; // expected-note {{because variant field 'n' has a non-trivial destructor}} }; }; -A2 a2; // until26-error {{deleted function}} +A2 a2; // expected-error {{deleted function}} union A3 { A3(); - NonTrivDtor n[3]; // until26-note {{because variant field 'n' has a non-trivial destructor}} + NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a non-trivial destructor}} }; -A3 a3; // until26-error {{deleted function}} +A3 a3; // expected-error {{deleted function}} struct A4 { A4(); union { - NonTrivDtor n[3]; // until26-note {{because variant field 'n' has a non-trivial destructor}} + NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a non-trivial destructor}} }; }; -A4 a4; // until26-error {{deleted function}} +A4 a4; // expected-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. @@ -64,18 +64,18 @@ struct B4 { B4 b4; // expected-error {{deleted function}} union B5 { B5(); - 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}} + 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}} }; }; -B5 b5; // until26-error {{deleted function}} +B5 b5; // expected-error {{deleted function}} union B6 { B6(); - 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}} + 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}} }; }; -B6 b6; // until26-error {{deleted function}} +B6 b6; // expected-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}} diff --git a/clang/test/CXX/special/class.dtor/p7.cpp b/clang/test/CXX/special/class.dtor/p7.cpp index 1b50bdc3920e0..c19bfcc5b119e 100644 --- a/clang/test/CXX/special/class.dtor/p7.cpp +++ b/clang/test/CXX/special/class.dtor/p7.cpp @@ -1,42 +1,42 @@ // RUN: %clang_cc1 -verify -std=c++26 %s -Wno-defaulted-function-deleted -triple x86_64-linux-gnu struct NonTrivial { - NonTrivial(int) { } - ~NonTrivial() { } + NonTrivial(int) { } + ~NonTrivial() { } }; union U0 { - NonTrivial nt; - int i; - }; - U0 u0; + NonTrivial nt; + int i; +}; +U0 u0; // overload resolution to select a constructor to default-initialize an object of type X either fails union U1 { - U1(int); - NonTrivial nt; + U1(int); + NonTrivial nt; // #1 }; -U1 u1(1); // expected-error {{deleted function}} +U1 u1(1); // expected-error {{deleted function}} expected-note@#1 {{non-trivial destructor}} // or selects a constructor that is either deleted or not trivial, or union U2 { - U2() : nt(2) { } - NonTrivial nt; + U2() : nt(2) { } + NonTrivial nt; // #2 }; -U2 u2; // expected-error {{deleted function}} +U2 u2; // expected-error {{deleted function}} expected-note@#2 {{non-trivial destructor}} union U3 { - U3() = delete; - U3(int); - NonTrivial nt; + U3() = delete; + U3(int); + NonTrivial nt; // #3 }; -U3 u3(1); // expected-error {{deleted function}} +U3 u3(1); // expected-error {{deleted function}} expected-note@#3 {{non-trivial destructor}} // or X has a variant member V of class type M (or possibly multi-dimensional array thereof) where V has a default member initializer and M has a destructor that is non-trivial, union U4 { - NonTrivial nt = 1; + NonTrivial nt = 1; // #4 }; -U4 u4; // expected-error {{deleted function}} +U4 u4; // expected-error {{deleted function}} expected-note@#4 {{non-trivial destructor}} union U5 { NonTrivial nt; @@ -44,5 +44,12 @@ union U5 { }; U5 u5; +union U6 { + U6() = default; + NonTrivial nt; + U6* next = nullptr; +}; +U6 u6; + >From 80e24760503a1935ea8f2d83f34561f95c9cbf52 Mon Sep 17 00:00:00 2001 From: Barry Revzin <barry.rev...@gmail.com> Date: Thu, 3 Jul 2025 10:29:19 -0500 Subject: [PATCH 5/5] Test fixup --- clang/test/CXX/drs/cwg6xx.cpp | 8 ++++++-- clang/test/CXX/special/class.ctor/p5-0x.cpp | 15 ++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp index dd54bb5295b56..d7437fab1c183 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,cxx26 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors #if __cplusplus == 199711L #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) @@ -922,7 +922,11 @@ namespace cwg667 { // cwg667: 8 struct B { ~B() = delete; }; union C { B b; }; - static_assert(!__is_trivially_destructible(C), ""); // cxx26-error {{failed}} + #if __cplusplus > 202302L + static_assert(__is_trivially_destructible(C), ""); + #else + static_assert(!__is_trivially_destructible(C), ""); + #endif 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 97a8b035a0705..f00ec4ddc6350 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=expected,until26 %s -std=c++11 -Wno-deprecated-builtins -Wno-defaulted-function-deleted +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11-23 %s -std=c++11 -Wno-deprecated-builtins -Wno-defaulted-function-deleted +// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11-23 %s -std=c++23 -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 {}; @@ -24,8 +25,8 @@ int n; // - X is a union-like class that has a variant member with a non-trivial // 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 Deleted1a { UserProvidedDefCtor u; }; // cxx11-23-note {{default constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has a non-trivial default constructor}} +Deleted1a d1a; // cxx11-23-error {{implicitly-deleted default constructor}} union NotDeleted1a { DefaultedDefCtor1 nu; }; NotDeleted1a nd1a; union NotDeleted1b { DefaultedDefCtor2 nu; }; @@ -87,19 +88,19 @@ NotDeleted3i nd3i; union Deleted4a { const int a; const int b; - const UserProvidedDefCtor c; // until26-note {{because variant field 'c' has a non-trivial default constructor}} + const UserProvidedDefCtor c; // cxx11-23-note {{because variant field 'c' has a non-trivial default constructor}} }; -Deleted4a d4a; // until26-error {{implicitly-deleted default constructor}} +Deleted4a d4a; // cxx11-23-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; }; // until26-note {{because all data members of an anonymous union member are const-qualified}} + union { const int a; }; // cxx11-23-note {{because all data members of an anonymous union member are const-qualified}} union { int b; }; }; -Deleted5a d5a; // until26-error {{implicitly-deleted default constructor}} +Deleted5a d5a; // cxx11-23-error {{implicitly-deleted default constructor}} struct NotDeleted5a { union { const int a; int b; }; union { const int c; int d; }; }; NotDeleted5a nd5a; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits