[clang] 435d084 - [Clang][Sema] Fix incorrect rejection default construction of union with nontrivial member (#82407)

via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 6 13:44:09 PDT 2025


Author: Shafik Yaghmour
Date: 2025-10-06T13:44:05-07:00
New Revision: 435d084ed695e7e8d352e22e8b8614b6a022bb04

URL: https://github.com/llvm/llvm-project/commit/435d084ed695e7e8d352e22e8b8614b6a022bb04
DIFF: https://github.com/llvm/llvm-project/commit/435d084ed695e7e8d352e22e8b8614b6a022bb04.diff

LOG: [Clang][Sema] Fix incorrect rejection default construction of union with nontrivial member (#82407)

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

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/drs/cwg20xx.cpp
    clang/test/CodeGen/union-non-trivial-member.cpp
    clang/test/SemaCXX/cxx0x-nontrivial-union.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 05379f44a0a39..390e0fa7a9a2b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -445,6 +445,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 d27f76706e5ee..215431ca71310 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9546,14 +9546,32 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
   CXXMethodDecl *Decl = SMOR.getMethod();
   FieldDecl *Field = Subobj.dyn_cast<FieldDecl*>();
 
-  int DiagKind = -1;
-
-  if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted)
-    DiagKind = !Decl ? 0 : 1;
-  else if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
-    DiagKind = 2;
+  enum {
+    NotSet = -1,
+    NoDecl,
+    DeletedDecl,
+    MultipleDecl,
+    InaccessibleDecl,
+    NonTrivialDecl
+  } DiagKind = NotSet;
+
+  if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted) {
+    if (CSM == CXXSpecialMemberKind::DefaultConstructor && 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())
+        return false;
+    }
+    DiagKind = !Decl ? NoDecl : DeletedDecl;
+  } else if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
+    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.
@@ -9569,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) {
@@ -9593,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;

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>&);

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 4bb012f6e4247..1eb7e3a2dea8b 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


        


More information about the cfe-commits mailing list