diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index ccb308e103253..5c235f719d8f9 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 + bool TrivialUnion = Context.getLangOpts().CPlusPlus26 && isUnion(); + if (const auto *RecordTy = T->getAs()) { auto *FieldRec = cast(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..c4b23b06a7978 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9543,6 +9543,45 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( if (DiagKind == -1) return false; + if (S.LangOpts.CPlusPlus26 && inUnion() && + CSM == CXXSpecialMemberKind::Destructor) { + // [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, + + RecordDecl *Parent = Field->getParent(); + while (Parent && (Parent->isAnonymousStructOrUnion() || + ((Parent->isUnion() || Parent->isStruct()) && + Parent->getIdentifier() == nullptr))) { + if (auto RD = dyn_cast_or_null(Parent->getParent())) + Parent = RD; + else + break; + } + + auto ParentDecl = cast(Parent); + if (!ParentDecl->isBeingDefined()) { + Sema::SpecialMemberOverloadResult SMOR = S.LookupSpecialMember( + ParentDecl, CXXSpecialMemberKind::DefaultConstructor, false, false, + false, false, false); + if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Success) { + CXXConstructorDecl *Ctor = + dyn_cast(SMOR.getMethod()); + if (Ctor->isTrivial()) + return false; + + if (!Ctor->isUserProvided() && !Field->hasInClassInitializer()) + return false; + } + } + } + if (Diagnose) { if (Field) { S.Diag(Field->getLocation(), @@ -9696,6 +9735,10 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD)) return true; + if (inUnion() && S.LangOpts.CPlusPlus26 && + CSM == CXXSpecialMemberKind::DefaultConstructor) + return false; + 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. @@ -9774,7 +9817,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.LangOpts.CPlusPlus26) { if (Diagnose) S.Diag(FieldRecord->getLocation(), diag::note_deleted_default_ctor_all_const) @@ -9804,6 +9848,10 @@ bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() { // default constructor. Don't do that. if (CSM == CXXSpecialMemberKind::DefaultConstructor && inUnion() && AllFieldsAreConst) { + + if (S.LangOpts.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..d7437fab1c183 100644 --- a/clang/test/CXX/drs/cwg6xx.cpp +++ b/clang/test/CXX/drs/cwg6xx.cpp @@ -922,7 +922,11 @@ namespace cwg667 { // cwg667: 8 struct B { ~B() = delete; }; union C { B b; }; + #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 e0c53058f892b..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,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %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 {}; struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; }; @@ -23,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; }; // 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; }; // 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; }; @@ -86,19 +88,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; // cxx11-23-note {{because variant field 'c' has a non-trivial default constructor}} }; -Deleted4a d4a; // expected-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; }; // expected-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; // expected-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; diff --git a/clang/test/CXX/special/class.ctor/p6-0x.cpp b/clang/test/CXX/special/class.ctor/p6-0x.cpp index 156a2b20c6b52..3ad7ae32c1314 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,cxx11-23 %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(); // cxx11-23-error {{follows non-constexpr declaration}} cxx26-error {{missing exception specification}} + friend constexpr NonConstexpr2::NonConstexpr2(); // cxx11-23-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 { // cxx11-23-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}} cxx11-23-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(); // cxx11-23-error {{non-constexpr declaration of 'A' follows constexpr declaration}} cxx26-error {{missing exception specification}} }; namespace UnionCtors { - union A { // expected-note {{here}} + union A { // cxx11-23-note {{here}} int a; int b; }; @@ -79,7 +81,7 @@ namespace UnionCtors { int d = 5; }; }; - struct E { // expected-note {{here}} + struct E { // cxx11-23-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; // cxx11-23-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; // cxx11-23-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; }; // cxx11-23-note {{field 'h' has a non-trivial default constructor}} + V v; // cxx11-23-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..655ffda48dcae 100644 --- a/clang/test/CXX/special/class.dtor/p5-0x.cpp +++ b/clang/test/CXX/special/class.dtor/p5-0x.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -verify -std=c++11 %s -Wno-defaulted-function-deleted -triple x86_64-linux-gnu +// RUN: %clang_cc1 -verify=expected -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(); 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..90ae8eadfc225 --- /dev/null +++ b/clang/test/CXX/special/class.dtor/p7.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -verify -std=c++26 %s -Wno-defaulted-function-deleted -triple x86_64-linux-gnu + +struct NonTrivial { + int i; + constexpr NonTrivial(int i) :i(i) { } + constexpr ~NonTrivial() { } +}; + +union U0 { + NonTrivial nt; + int i; +}; +U0 u0; + +// check for constant evaluation failure +constexpr NonTrivial make() { + U0 u0; + return u0.nt; +} +constexpr NonTrivial nt = make(); // expected-error {{must be initialized by a constant expression}}} + // expected-note@-3 {{union with no active member}} + // expected-note@-4 {{in call to 'NonTrivial(u0.nt)'}} + // expected-note@-3 {{in call to 'make()'}} + +// overload resolution to select a constructor to default-initialize an object of type X either fails +union U1 { + U1(int); + NonTrivial nt; // #1 +}; +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; // #2 +}; +U2 u2; // expected-error {{deleted function}} expected-note@#2 {{non-trivial destructor}} + +union U3 { + U3() = delete; + U3(int); + NonTrivial nt; // #3 +}; +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; // #4 +}; +U4 u4; // expected-error {{deleted function}} expected-note@#4 {{non-trivial destructor}} + +union U5 { + NonTrivial nt[2] = {1, 2}; // #5 +}; +U5 u5; // expected-error {{deleted function}} expected-note@#5 {{non-trivial destructor}} + +union U6 { + NonTrivial nt; + U6* next = nullptr; +}; +U6 u6; + +union U7 { + U7() = default; + NonTrivial nt; + U6* next = nullptr; +}; +U7 u7; + +union U8 { + struct { + NonTrivial x; + }; +} u8; + +union U9 { + struct { + NonTrivial x = 1; // #6 + }; +} u9; // expected-error {{deleted function}} + +struct DeletedDtor { + ~DeletedDtor() = delete; // expected-note 2 {{deleted here}} +}; +union B1 { + B1(); + DeletedDtor a; // expected-note {{because field 'a' has a deleted destructor}} +}; +B1 b1; // expected-error {{deleted function}} +union B2 { + B2(); + union { // expected-note {{deleted destructor}} + DeletedDtor a; // expected-note {{because field 'a' has a deleted destructor}} + }; +}; +B2 b2; // expected-error {{deleted function}}