Skip to content
9 changes: 6 additions & 3 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const bool TrivialUnion = Context.getLangOpts().CPlusPlus26 && isUnion();
bool TrivialUnion = Context.getLangOpts().CPlusPlus26 && isUnion();

nit (we typically don’t use top-level const for local vars)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? I see a lot of them btw.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it was in our coding standards because I’m used to people pointing this out all the time but https://llvm.org/docs/CodingStandards.html doesn’t seem to have anything to say about it.


if (const auto *RecordTy = T->getAs<RecordType>()) {
auto *FieldRec = cast<CXXRecordDecl>(RecordTy->getDecl());
if (FieldRec->getDefinition()) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 15 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()))
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CXX/drs/cwg6xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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 {};
Expand Down
15 changes: 8 additions & 7 deletions clang/test/CXX/special/class.ctor/p5-0x.cpp
Original file line number Diff line number Diff line change
@@ -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; };
Expand All @@ -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; };
Expand Down Expand Up @@ -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;

Expand Down
28 changes: 15 additions & 13 deletions clang/test/CXX/special/class.ctor/p6-0x.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 {};
Expand All @@ -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;
Expand All @@ -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;
};
Expand All @@ -79,19 +81,19 @@ namespace UnionCtors {
int d = 5;
};
};
struct E { // expected-note {{here}}
struct E { // until26-note {{here}}
union {
int a;
int b;
};
};

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}}
};
}

Expand Down Expand Up @@ -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}}
}
33 changes: 17 additions & 16 deletions clang/test/CXX/special/class.dtor/p5-0x.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
Expand All @@ -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}}
Expand Down
48 changes: 48 additions & 0 deletions clang/test/CXX/special/class.dtor/p7.cpp
Original file line number Diff line number Diff line change
@@ -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;



Loading