[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