[clang] [clang][Sema] Track trivial-relocatability as a type trait (PR #84621)
@@ -857,8 +881,13 @@ void CXXRecordDecl::addedMember(Decl *D) { data().HasDeclaredCopyAssignmentWithConstParam = true; } -if (Method->isMoveAssignmentOperator()) +if (Method->isMoveAssignmentOperator()) { SMKind |= SMF_MoveAssignment; +} + +if (Method->isUserProvided() && +(Method->isCopyAssignment() || Method->isMoveAssignment())) + data().IsNaturallyTriviallyRelocatable = false; brevzin wrote: > Only "stupid types" will have a trivial copy/move constructor and a > non-trivial assignment operator that does something different. One such type is `std::tuple`. As far as I can tell, the way the different papers handle this type is: * P1144: not trivially relocatable (because the copy/move assignment operator is user-provided) * P2786: trivially relocatable (because we only care about move construction, and that one is defaulted) I don't actually know what goes wrong (if anything) if you make `tuple` trivially relocatable, but at least that's a nice concrete example to think about. https://github.com/llvm/llvm-project/pull/84621 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/8] [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()) { 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..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(); + // 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_cc
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/9] [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()) { 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..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(); + // 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_cc
[clang] [Clang] Partial implementation of support for P3074 (trivial unions) (PR #146815)
https://github.com/brevzin edited 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
[clang] [Clang] Partial implementation of support for P3074 (trivial unions) (PR #146815)
@@ -9543,6 +9543,48 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( if (DiagKind == -1) return false; + if (this->S.Context.getLangOpts().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->getIdentifier() == nullptr))) { + if (auto RD = dyn_cast_or_null(Parent->getParent())) { +Parent = RD; + } else { +break; + } +} + +auto ParentDecl = dyn_cast(Parent); +if (!ParentDecl->isBeingDefined()) { + Sema::SpecialMemberOverloadResult SMOR = S.LookupSpecialMember( + ParentDecl, CXXSpecialMemberKind::DefaultConstructor, false, false, + false, false, false); + if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Success) { brevzin wrote: I'm guessing this is the _wrong_ way to do this, but I don't know how to do it better. Also this doesn't exactly implement the wording I quoted above because the wording is just wrong (see future CWG issue [here](https://github.com/cplusplus/cwg/issues/722)). 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
[clang] [Clang] Partial implementation of support for P3074 (trivial unions) (PR #146815)
@@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify -std=c++26 %s -Wno-defaulted-function-deleted -triple x86_64-linux-gnu + +struct NonTrivial { brevzin wrote: Done. 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
[clang] [Clang] Partial implementation of support for P3074 (trivial unions) (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 01/10] [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()) { 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..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(); + // 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_
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/6] [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()) { 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..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(); + // 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_cc
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/7] [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()) { 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..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(); + // 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_cc
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 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()) { 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..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(); + // 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_cc
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
@@ -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}} brevzin wrote: Done. 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
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
@@ -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}} brevzin wrote: Done. I just added the C++23 line too, since the test starts at C++11. 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
[clang] [P3074] Parital implementation of support for trivial unions (PR #146815)
https://github.com/brevzin created https://github.com/llvm/llvm-project/pull/146815 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. >From d070148afae5aa5e325b34c02e2d0ffb67325d7e Mon Sep 17 00:00:00 2001 From: Barry Revzin Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH] [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()) { 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..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(); + // 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 -
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
https://github.com/brevzin edited 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
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
brevzin wrote: Oh. Actually I didn't quite properly implement the destructor rule, since that changed since I originally implemented this. The new rule is that the default destructor for a union `X` [is deleted if](http://eel.is/c++draft/class.dtor#7.2) * default constructing an `X` either fails, or is deleted, or is non trivial, or * one of the members `M` of `X` has a default member initializer and `M`'s destructor is non-trivial. I don't know how to do that yet, so plz don't merge (although I suspect there are other changes to be made). 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
[clang] [P3074] Partial implementation of support for trivial unions (PR #146815)
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 Date: Fri, 27 Jun 2025 17:29:45 -0500 Subject: [PATCH 1/2] [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()) { 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..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(); + // 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_cc