mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 00:56:41 +00:00
[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
This commit is contained in:
parent
8e94f0a0ad
commit
5ae5774fb0
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user