Skip to content

Commit 44c9e4b

Browse files
committed
[Sema] Fix handling of fields with initializers in nested anonymous unions.
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
1 parent 7115ed0 commit 44c9e4b

File tree

3 files changed

+44
-12
lines changed

3 files changed

+44
-12
lines changed

clang/lib/Sema/SemaInit.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -813,19 +813,13 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
813813

814814
if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) {
815815
const RecordDecl *RDecl = RType->getDecl();
816-
if (RDecl->isUnion() && ILE->getInitializedFieldInUnion())
816+
if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) {
817817
FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(),
818818
Entity, ILE, RequiresSecondPass, FillWithNoInit);
819-
else if (RDecl->isUnion() && isa<CXXRecordDecl>(RDecl) &&
820-
cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) {
821-
for (auto *Field : RDecl->fields()) {
822-
if (Field->hasInClassInitializer()) {
823-
FillInEmptyInitForField(0, Field, Entity, ILE, RequiresSecondPass,
824-
FillWithNoInit);
825-
break;
826-
}
827-
}
828819
} else {
820+
assert((!RDecl->isUnion() || !isa<CXXRecordDecl>(RDecl) ||
821+
!cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) &&
822+
"We should have computed initialized fields already");
829823
// The fields beyond ILE->getNumInits() are default initialized, so in
830824
// order to leave them uninitialized, the ILE is expanded and the extra
831825
// fields are then filled with NoInitExpr.
@@ -2163,12 +2157,15 @@ void InitListChecker::CheckStructUnionTypes(
21632157
return;
21642158
for (RecordDecl::field_iterator FieldEnd = RD->field_end();
21652159
Field != FieldEnd; ++Field) {
2166-
if (Field->hasInClassInitializer()) {
2160+
if (Field->hasInClassInitializer() ||
2161+
(Field->isAnonymousStructOrUnion() &&
2162+
Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) {
21672163
StructuredList->setInitializedFieldInUnion(*Field);
21682164
// FIXME: Actually build a CXXDefaultInitExpr?
21692165
return;
21702166
}
21712167
}
2168+
llvm_unreachable("Couldn't find in-class initializer");
21722169
}
21732170

21742171
// Value-initialize the first member of the union that isn't an unnamed

clang/test/AST/ast-dump-APValue-anon-union.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void Test() {
3636

3737
constexpr U0 u0a{};
3838
// CHECK: | `-VarDecl {{.*}} <col:{{.*}}, col:{{.*}}> col:{{.*}} u0a 'const U0' constexpr listinit
39-
// CHECK-NEXT: | |-value: Union None
39+
// CHECK-NEXT: | |-value: Union .U0::(anonymous union at {{.*}}) Union .f Float 3.141500e+00
4040

4141
constexpr U0 u0b{3.1415f};
4242
// CHECK: | `-VarDecl {{.*}} <col:{{.*}}, col:{{.*}}> col:{{.*}} u0b 'const U0' constexpr listinit

clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,38 @@ namespace use_self {
7777

7878
int fib(int n) { return FibTree{n}.v; }
7979
}
80+
81+
namespace nested_union {
82+
union Test1 {
83+
union {
84+
int inner { 42 };
85+
};
86+
int outer;
87+
};
88+
static_assert(Test1{}.inner == 42, "");
89+
struct Test2 {
90+
union {
91+
struct {
92+
int inner : 32 { 42 }; // expected-warning {{C++20 extension}}
93+
};
94+
int outer;
95+
};
96+
};
97+
static_assert(Test2{}.inner == 42, "");
98+
struct Int { int x; };
99+
struct Test3 {
100+
int x;
101+
union {
102+
struct {
103+
const int& y;
104+
int inner : 32 { 42 }; // expected-warning {{C++20 extension}}
105+
};
106+
int outer;
107+
};
108+
};
109+
constexpr char f(Test3) { return 1; } // expected-note {{candidate function}}
110+
constexpr char f(Int) { return 2; } // expected-note {{candidate function}}
111+
// FIXME: This shouldn't be ambiguous; either we should reject the declaration
112+
// of Test3, or we should exclude f(Test3) as a candidate.
113+
static_assert(f({1}) == 2, ""); // expected-error {{call to 'f' is ambiguous}}
114+
}

0 commit comments

Comments
 (0)