Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

shafik
Copy link
Collaborator

@shafik shafik commented Feb 20, 2024

In 765d8a1 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: #81774

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 20, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 20, 2024

@llvm/pr-subscribers-clang

Author: Shafik Yaghmour (shafik)

Changes

In 765d8a1 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: #81774


Full diff: https://github.com/llvm/llvm-project/pull/82407.diff

4 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+15-3)
  • (modified) clang/test/CodeGen/union-non-trivial-member.cpp (+17)
  • (modified) clang/test/SemaCXX/cxx0x-nontrivial-union.cpp (+11)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5bca2c965c866b..452382eb6c4a1e 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 79263bc3ff671d..25a4b4381ca25e 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 fdc9fd16911e14..8b055a9970fc75 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 c7cdf76d850dbe..141fe96f512471 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 b/c NoState has a brace-or-equal-initializer
+union a {
+        int NoState = 0;
+        Handle CustomState;
+} b;
+} // namespace GH81774

// initializer
const auto *RD = cast<CXXRecordDecl>(Field->getParent());
if (!RD->hasInClassInitializer())
DiagKind = !Decl ? 0 : 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what is going on here? this is the same value as its 'else' clause, and leaves DiagKind as -1 otherwise? I might suggest making this a single 'if' and getting rid of the 'else' here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have three different states I need to check, I can remove a check by setting DiagKind unconditionally and then check if(RD->hasInClassInitializer()) instead and in that case set it back to -1 but that does not express the intent as clearly. So I think what I have now is more maintainable although slightly more verbose.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I ran into exactly the same issue that Erich did in noticing that both branches do DiagKind = !Decl ? 0 : 1;. That two of us ran into the same readability issue suggests that this should be modified to be more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can refactor it like this: MitalAshok@a119d89 and I would suggest changing to an enum for readability: MitalAshok@38ca82e (This can then be extracted out into a static function that returns the enum and each DiagKind = ... can turn into return ..., and the if/else if ladder can be dismantled)

…ith nontrivial member

In 765d8a1 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: llvm#81774
@shafik shafik force-pushed the fixdincorrectdelofctoronsomeunionspart2 branch 2 times, most recently from e1bf9ef to 9894851 Compare February 22, 2024 02:20
@shafik shafik force-pushed the fixdincorrectdelofctoronsomeunionspart2 branch from 9894851 to af9639f Compare February 22, 2024 05:55
@shafik shafik force-pushed the fixdincorrectdelofctoronsomeunionspart2 branch from ab431f4 to 3e5db7b Compare February 24, 2024 05:15
@MitalAshok
Copy link
Contributor

This is CWG2084. Could you also add some tests to CXX/drs/ so that www/cxx_dr_status.html can be updated?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang incorrectly rejects default construction of union with nontrivial member, part 2
5 participants