[clang] [Clang][Sema] Fix incorrect rejection default construction of union with nontrivial member (PR #82407)
Shafik Yaghmour via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 3 13:30:13 PDT 2025
https://github.com/shafik updated https://github.com/llvm/llvm-project/pull/82407
>From 5fcaeaddccc0f7e370bf7bebce113d8d52e1b1bd Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghmour at intel.com>
Date: Tue, 20 Feb 2024 11:22:39 -0800
Subject: [PATCH 1/3] [Clang][Sema] Fix incorrect rejection default
construction of union with nontrivial member
In 765d8a192180f8f33618087b15c022fe758044af we impelemented a fix for incorrect deletion of
default constructors in unions. This fix missed a case and so this PR will
extend the fix to cover the additional case.
Fixes: https://github.com/llvm/llvm-project/issues/81774
---
clang/docs/ReleaseNotes.rst | 3 +++
clang/lib/Sema/SemaDeclCXX.cpp | 18 +++++++++++++++---
.../test/CodeGen/union-non-trivial-member.cpp | 17 +++++++++++++++++
clang/test/SemaCXX/cxx0x-nontrivial-union.cpp | 11 +++++++++++
4 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5bca2c965c866..452382eb6c4a1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -292,6 +292,9 @@ Bug Fixes to C++ Support
was only accepted at namespace scope but not at local function scope.
- Clang no longer tries to call consteval constructors at runtime when they appear in a member initializer.
(`#782154 <https://github.com/llvm/llvm-project/issues/82154>`_`)
+- Fix for clang incorrectly rejecting the default construction of a union with
+ nontrivial member when another member has an initializer.
+ (`#81774 <https://github.com/llvm/llvm-project/issues/81774>`_)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 79263bc3ff671..25a4b4381ca25 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9442,9 +9442,21 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
int DiagKind = -1;
- if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted)
- DiagKind = !Decl ? 0 : 1;
- else if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
+ if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted) {
+ if (CSM == Sema::CXXDefaultConstructor && Field &&
+ Field->getParent()->isUnion()) {
+ // [class.default.ctor]p2:
+ // A defaulted default constructor for class X is defined as deleted if
+ // - X is a union that has a variant member with a non-trivial default
+ // constructor and no variant member of X has a default member
+ // initializer
+ const auto *RD = cast<CXXRecordDecl>(Field->getParent());
+ if (!RD->hasInClassInitializer())
+ DiagKind = !Decl ? 0 : 1;
+ } else {
+ DiagKind = !Decl ? 0 : 1;
+ }
+ } else if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
DiagKind = 2;
else if (!isAccessible(Subobj, Decl))
DiagKind = 3;
diff --git a/clang/test/CodeGen/union-non-trivial-member.cpp b/clang/test/CodeGen/union-non-trivial-member.cpp
index fdc9fd16911e1..8b055a9970fc7 100644
--- a/clang/test/CodeGen/union-non-trivial-member.cpp
+++ b/clang/test/CodeGen/union-non-trivial-member.cpp
@@ -15,14 +15,25 @@ union UnionNonTrivial {
non_trivial_constructor b{};
};
+struct Handle {
+ Handle(int) {}
+};
+
+union UnionNonTrivialEqualInit {
+ int NoState = 0;
+ Handle CustomState;
+};
+
void f() {
UnionInt u1;
UnionNonTrivial u2;
+ UnionNonTrivialEqualInit u3;
}
// CHECK: define dso_local void @_Z1fv()
// CHECK: call void @_ZN8UnionIntC1Ev
// CHECK-NEXT: call void @_ZN15UnionNonTrivialC1Ev
+// CHECK-NEXT: call void @_ZN24UnionNonTrivialEqualInitC1Ev
// CHECK: define {{.*}}void @_ZN8UnionIntC1Ev
// CHECK: call void @_ZN8UnionIntC2Ev
@@ -30,8 +41,14 @@ void f() {
// CHECK: define {{.*}}void @_ZN15UnionNonTrivialC1Ev
// CHECK: call void @_ZN15UnionNonTrivialC2Ev
+// CHECK: define {{.*}}void @_ZN24UnionNonTrivialEqualInitC1Ev
+// CHECK: call void @_ZN24UnionNonTrivialEqualInitC2Ev
+
// CHECK: define {{.*}}void @_ZN8UnionIntC2Ev
// CHECK: store i32 1000
// CHECK: define {{.*}}void @_ZN15UnionNonTrivialC2Ev
// CHECK: call void @_ZN23non_trivial_constructorC1Ev
+
+// CHECK: define {{.*}}void @_ZN24UnionNonTrivialEqualInitC2Ev
+// CHECK: store i32 0
diff --git a/clang/test/SemaCXX/cxx0x-nontrivial-union.cpp b/clang/test/SemaCXX/cxx0x-nontrivial-union.cpp
index c7cdf76d850db..833642b3d739a 100644
--- a/clang/test/SemaCXX/cxx0x-nontrivial-union.cpp
+++ b/clang/test/SemaCXX/cxx0x-nontrivial-union.cpp
@@ -188,3 +188,14 @@ static_assert(U2().b.x == 100, "");
static_assert(U3().b.x == 100, "");
} // namespace GH48416
+
+namespace GH81774 {
+struct Handle {
+ Handle(int) {}
+};
+// Should be well-formed because NoState has a brace-or-equal-initializer.
+union a {
+ int NoState = 0;
+ Handle CustomState;
+} b;
+} // namespace GH81774
>From 409f2e9b47ec265f6e2ca6929219962c71bb6ed1 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghmour at intel.com>
Date: Thu, 2 Oct 2025 17:41:57 -0700
Subject: [PATCH 2/3] Address feedback.
---
clang/docs/ReleaseNotes.rst | 3 ++-
clang/lib/Sema/SemaDeclCXX.cpp | 32 +++++++++++++++++++-------------
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ac2a46a95a32d..caf357e557cec 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -402,7 +402,6 @@ Bug Fixes to Attribute Support
Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
-
- Diagnose binding a reference to ``*nullptr`` during constant evaluation. (#GH48665)
- Suppress ``-Wdeprecated-declarations`` in implicitly generated functions. (#GH147293)
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
@@ -439,6 +438,8 @@ Bug Fixes to C++ Support
- Correctly deduce return types in ``decltype`` expressions. (#GH160497) (#GH56652) (#GH116319) (#GH161196)
- Fixed a crash in the pre-C++23 warning for attributes before a lambda declarator (#GH161070).
- Fix a crash when attempting to deduce a deduction guide from a non deducible template template parameter. (#130604)
+- Fix for clang incorrectly rejecting the default construction of a union with
+ nontrivial member when another member has an initializer. (#GH81774)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 9443d1bb580d2..b37f93ed9cbb1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9546,10 +9546,17 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
CXXMethodDecl *Decl = SMOR.getMethod();
FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>();
- int DiagKind = -1;
+ enum {
+ NotSet = -1,
+ NoDecl,
+ DeletedDecl,
+ MultipleDecl,
+ InaccessibleDecl,
+ NonTrivialDecl
+ } DiagKind = NotSet;
if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted) {
- if (CSM == Sema::CXXDefaultConstructor && Field &&
+ if (CSM == CXXSpecialMemberKind::DefaultConstructor && Field &&
Field->getParent()->isUnion()) {
// [class.default.ctor]p2:
// A defaulted default constructor for class X is defined as deleted if
@@ -9557,15 +9564,14 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
// constructor and no variant member of X has a default member
// initializer
const auto *RD = cast<CXXRecordDecl>(Field->getParent());
- if (!RD->hasInClassInitializer())
- DiagKind = !Decl ? 0 : 1;
- } else {
- DiagKind = !Decl ? 0 : 1;
+ if (RD->hasInClassInitializer())
+ return false;
}
+ DiagKind = !Decl ? NoDecl : DeletedDecl;
} else if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
- DiagKind = 2;
+ DiagKind = MultipleDecl;
else if (!isAccessible(Subobj, Decl))
- DiagKind = 3;
+ DiagKind = InaccessibleDecl;
else if (!IsDtorCallInCtor && Field && Field->getParent()->isUnion() &&
!Decl->isTrivial()) {
// A member of a union must have a trivial corresponding special member.
@@ -9581,13 +9587,13 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
// initializer
const auto *RD = cast<CXXRecordDecl>(Field->getParent());
if (!RD->hasInClassInitializer())
- DiagKind = 4;
+ DiagKind = NonTrivialDecl;
} else {
- DiagKind = 4;
+ DiagKind = NonTrivialDecl;
}
}
- if (DiagKind == -1)
+ if (DiagKind == NotSet)
return false;
if (Diagnose) {
@@ -9605,9 +9611,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
<< /*IsObjCPtr*/ false;
}
- if (DiagKind == 1)
+ if (DiagKind == DeletedDecl)
S.NoteDeletedFunction(Decl);
- // FIXME: Explain inaccessibility if DiagKind == 3.
+ // FIXME: Explain inaccessibility if DiagKind == InaccessibleDecl.
}
return true;
>From 5ac1c34148559378ed11fdd08456e9f6bfaa9868 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik.yaghmour at intel.com>
Date: Fri, 3 Oct 2025 13:29:25 -0700
Subject: [PATCH 3/3] This overlaps w/ cwg2084, so updating dr tests.
---
clang/test/CXX/drs/cwg20xx.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 141a1012aef93..bd233bb09522f 100644
--- a/clang/test/CXX/drs/cwg20xx.cpp
+++ b/clang/test/CXX/drs/cwg20xx.cpp
@@ -401,6 +401,15 @@ namespace cwg2083 { // cwg2083: partial
#endif
} // namespace cwg2083
+namespace cwg2084 { // cwg2084: 3.1
+struct S {
+ S();
+};
+union U {
+ S s{}; // cxx98-error {{function definition does not declare parameters}}
+} u;
+} // namespace cwg2084
+
namespace cwg2091 { // cwg2091: 10
template<int &> struct X;
template<int &N> void f(X<N>&);
More information about the cfe-commits
mailing list