[clang] 5ae5774 - [Sema] Fix handling of fields with initializers in nested anonymous unions. (#91692)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 4 09:24:47 PDT 2024
Author: Eli Friedman
Date: 2024-06-04T09:24:44-07:00
New Revision: 5ae5774fb0b5cac11af479b0905dfdd5255b4047
URL: https://github.com/llvm/llvm-project/commit/5ae5774fb0b5cac11af479b0905dfdd5255b4047
DIFF: https://github.com/llvm/llvm-project/commit/5ae5774fb0b5cac11af479b0905dfdd5255b4047.diff
LOG: [Sema] Fix handling of fields with initializers in nested anonymous unions. (#91692)
Make sure we count the anonymous union as an initialized field, so we
properly construct the AST.
Included bonus testcase Test3, which shows a remaining gap: an anonymous
union can contain a partially initialized anonymous struct, and we
handle that inconsistently.
Fixes #91257
Added:
Modified:
clang/lib/Sema/SemaInit.cpp
clang/test/AST/ast-dump-APValue-anon-union.cpp
clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 79bdc8e9f8783..9ed3e8a0df025 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -814,19 +814,13 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) {
const RecordDecl *RDecl = RType->getDecl();
- if (RDecl->isUnion() && ILE->getInitializedFieldInUnion())
- FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(),
- Entity, ILE, RequiresSecondPass, FillWithNoInit);
- else if (RDecl->isUnion() && isa<CXXRecordDecl>(RDecl) &&
- cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) {
- for (auto *Field : RDecl->fields()) {
- if (Field->hasInClassInitializer()) {
- FillInEmptyInitForField(0, Field, Entity, ILE, RequiresSecondPass,
- FillWithNoInit);
- break;
- }
- }
+ if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) {
+ FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(), Entity, ILE,
+ RequiresSecondPass, FillWithNoInit);
} else {
+ assert((!RDecl->isUnion() || !isa<CXXRecordDecl>(RDecl) ||
+ !cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) &&
+ "We should have computed initialized fields already");
// The fields beyond ILE->getNumInits() are default initialized, so in
// order to leave them uninitialized, the ILE is expanded and the extra
// fields are then filled with NoInitExpr.
@@ -2164,12 +2158,15 @@ void InitListChecker::CheckStructUnionTypes(
return;
for (RecordDecl::field_iterator FieldEnd = RD->field_end();
Field != FieldEnd; ++Field) {
- if (Field->hasInClassInitializer()) {
+ if (Field->hasInClassInitializer() ||
+ (Field->isAnonymousStructOrUnion() &&
+ Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) {
StructuredList->setInitializedFieldInUnion(*Field);
// FIXME: Actually build a CXXDefaultInitExpr?
return;
}
}
+ llvm_unreachable("Couldn't find in-class initializer");
}
// Value-initialize the first member of the union that isn't an unnamed
diff --git a/clang/test/AST/ast-dump-APValue-anon-union.cpp b/clang/test/AST/ast-dump-APValue-anon-union.cpp
index 0e6466ee1fd73..ffe14ed7322de 100644
--- a/clang/test/AST/ast-dump-APValue-anon-union.cpp
+++ b/clang/test/AST/ast-dump-APValue-anon-union.cpp
@@ -36,7 +36,7 @@ void Test() {
constexpr U0 u0a{};
// CHECK: | `-VarDecl {{.*}} <col:{{.*}}, col:{{.*}}> col:{{.*}} u0a 'const U0' constexpr listinit
- // CHECK-NEXT: | |-value: Union None
+ // CHECK-NEXT: | |-value: Union .U0::(anonymous union at {{.*}}) Union .f Float 3.141500e+00
constexpr U0 u0b{3.1415f};
// CHECK: | `-VarDecl {{.*}} <col:{{.*}}, col:{{.*}}> col:{{.*}} u0b 'const U0' constexpr listinit
diff --git a/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp
index 3d5e7726a17e5..03a6800898d18 100644
--- a/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp
+++ b/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp
@@ -77,3 +77,41 @@ namespace use_self {
int fib(int n) { return FibTree{n}.v; }
}
+
+namespace nested_union {
+ union Test1 {
+ union {
+ int inner { 42 };
+ };
+ int outer;
+ };
+ static_assert(Test1{}.inner == 42, "");
+ struct Test2 {
+ union {
+ struct {
+ int inner : 32 { 42 }; // expected-warning {{C++20 extension}}
+ int inner_no_init;
+ };
+ int outer;
+ };
+ };
+ static_assert(Test2{}.inner == 42, "");
+ static_assert(Test2{}.inner_no_init == 0, "");
+ struct Int { int x; };
+ struct Test3 {
+ int x;
+ union {
+ struct { // expected-note {{in implicit initialization}}
+ const int& y; // expected-note {{uninitialized reference member is here}}
+ int inner : 32 { 42 }; // expected-warning {{C++20 extension}}
+ };
+ int outer;
+ };
+ };
+ Test3 test3 = {1}; // expected-error {{reference member of type 'const int &' uninitialized}}
+ constexpr char f(Test3) { return 1; } // expected-note {{candidate function}}
+ constexpr char f(Int) { return 2; } // expected-note {{candidate function}}
+ // FIXME: This shouldn't be ambiguous; either we should reject the declaration
+ // of Test3, or we should exclude f(Test3) as a candidate.
+ static_assert(f({1}) == 2, ""); // expected-error {{call to 'f' is ambiguous}}
+}
More information about the cfe-commits
mailing list