From 5fcaeaddccc0f7e370bf7bebce113d8d52e1b1bd Mon Sep 17 00:00:00 2001 From: Shafik Yaghmour 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 `_`) +- Fix for clang incorrectly rejecting the default construction of a union with + nontrivial member when another member has an initializer. + (`#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(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 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(); - 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(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(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 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 struct X; template void f(X&);