Skip to content

Commit 5cda366

Browse files
committed
Revert "[clang] Fix false positive -Wmissing-field-initializer for anonymous unions (#70829)"
This reverts commit a01307a and its follow-up fix 32d5221. It caused unexpected warnings emitted for nested designators in C.
1 parent cd1038a commit 5cda366

File tree

3 files changed

+65
-201
lines changed

3 files changed

+65
-201
lines changed

clang/lib/Sema/SemaInit.cpp

Lines changed: 62 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,7 @@ class InitListChecker {
465465
void FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
466466
const InitializedEntity &ParentEntity,
467467
InitListExpr *ILE, bool &RequiresSecondPass,
468-
bool FillWithNoInit = false,
469-
bool WarnIfMissing = false);
468+
bool FillWithNoInit = false);
470469
void FillInEmptyInitializations(const InitializedEntity &Entity,
471470
InitListExpr *ILE, bool &RequiresSecondPass,
472471
InitListExpr *OuterILE, unsigned OuterIndex,
@@ -655,16 +654,11 @@ void InitListChecker::FillInEmptyInitForBase(
655654
}
656655
}
657656

658-
static bool hasAnyDesignatedInits(const InitListExpr *IL) {
659-
return llvm::any_of(*IL, [=](const Stmt *Init) {
660-
return isa_and_nonnull<DesignatedInitExpr>(Init);
661-
});
662-
}
663-
664-
void InitListChecker::FillInEmptyInitForField(
665-
unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity,
666-
InitListExpr *ILE, bool &RequiresSecondPass, bool FillWithNoInit,
667-
bool WarnIfMissing) {
657+
void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
658+
const InitializedEntity &ParentEntity,
659+
InitListExpr *ILE,
660+
bool &RequiresSecondPass,
661+
bool FillWithNoInit) {
668662
SourceLocation Loc = ILE->getEndLoc();
669663
unsigned NumInits = ILE->getNumInits();
670664
InitializedEntity MemberEntity
@@ -732,52 +726,15 @@ void InitListChecker::FillInEmptyInitForField(
732726

733727
if (hadError || VerifyOnly) {
734728
// Do nothing
735-
} else {
736-
if (WarnIfMissing) {
737-
auto CheckAnonMember = [&](const FieldDecl *FD,
738-
auto &&CheckAnonMember) -> FieldDecl * {
739-
FieldDecl *Uninitialized = nullptr;
740-
RecordDecl *RD = FD->getType()->getAsRecordDecl();
741-
assert(RD && "Not anonymous member checked?");
742-
for (auto *F : RD->fields()) {
743-
FieldDecl *UninitializedFieldInF = nullptr;
744-
if (F->isAnonymousStructOrUnion())
745-
UninitializedFieldInF = CheckAnonMember(F, CheckAnonMember);
746-
else if (!F->isUnnamedBitfield() &&
747-
!F->getType()->isIncompleteArrayType() &&
748-
!F->hasInClassInitializer())
749-
UninitializedFieldInF = F;
750-
751-
if (RD->isUnion() && !UninitializedFieldInF)
752-
return nullptr;
753-
if (!Uninitialized)
754-
Uninitialized = UninitializedFieldInF;
755-
}
756-
return Uninitialized;
757-
};
758-
759-
FieldDecl *FieldToDiagnose = nullptr;
760-
if (Field->isAnonymousStructOrUnion())
761-
FieldToDiagnose = CheckAnonMember(Field, CheckAnonMember);
762-
else if (!Field->isUnnamedBitfield() &&
763-
!Field->getType()->isIncompleteArrayType())
764-
FieldToDiagnose = Field;
765-
766-
if (FieldToDiagnose)
767-
SemaRef.Diag(Loc, diag::warn_missing_field_initializers)
768-
<< FieldToDiagnose;
769-
}
770-
771-
if (Init < NumInits) {
772-
ILE->setInit(Init, MemberInit.getAs<Expr>());
773-
} else if (!isa<ImplicitValueInitExpr>(MemberInit.get())) {
774-
// Empty initialization requires a constructor call, so
775-
// extend the initializer list to include the constructor
776-
// call and make a note that we'll need to take another pass
777-
// through the initializer list.
778-
ILE->updateInit(SemaRef.Context, Init, MemberInit.getAs<Expr>());
779-
RequiresSecondPass = true;
780-
}
729+
} else if (Init < NumInits) {
730+
ILE->setInit(Init, MemberInit.getAs<Expr>());
731+
} else if (!isa<ImplicitValueInitExpr>(MemberInit.get())) {
732+
// Empty initialization requires a constructor call, so
733+
// extend the initializer list to include the constructor
734+
// call and make a note that we'll need to take another pass
735+
// through the initializer list.
736+
ILE->updateInit(SemaRef.Context, Init, MemberInit.getAs<Expr>());
737+
RequiresSecondPass = true;
781738
}
782739
} else if (InitListExpr *InnerILE
783740
= dyn_cast<InitListExpr>(ILE->getInit(Init))) {
@@ -845,36 +802,9 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
845802
}
846803
}
847804
} else {
848-
InitListExpr *SForm =
849-
ILE->isSyntacticForm() ? ILE : ILE->getSyntacticForm();
850805
// The fields beyond ILE->getNumInits() are default initialized, so in
851806
// order to leave them uninitialized, the ILE is expanded and the extra
852807
// fields are then filled with NoInitExpr.
853-
854-
// Some checks that are required for missing fields warning are bound to
855-
// how many elements the initializer list originally was provided; perform
856-
// them before the list is expanded.
857-
bool WarnIfMissingField =
858-
!SForm->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) &&
859-
ILE->getNumInits();
860-
861-
// Disable check for missing fields when designators are used in C to
862-
// match gcc behaviour.
863-
// FIXME: Should we emulate possible gcc warning bug?
864-
WarnIfMissingField &=
865-
SemaRef.getLangOpts().CPlusPlus || !hasAnyDesignatedInits(SForm);
866-
867-
if (OuterILE) {
868-
// When nested designators are present, there might be two nested init
869-
// lists created and only outer will contain designated initializer
870-
// expression, so check outer list as well.
871-
InitListExpr *OuterSForm = OuterILE->isSyntacticForm()
872-
? OuterILE
873-
: OuterILE->getSyntacticForm();
874-
WarnIfMissingField &= SemaRef.getLangOpts().CPlusPlus ||
875-
!hasAnyDesignatedInits(OuterSForm);
876-
}
877-
878808
unsigned NumElems = numStructUnionElements(ILE->getType());
879809
if (!RDecl->isUnion() && RDecl->hasFlexibleArrayMember())
880810
++NumElems;
@@ -902,7 +832,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
902832
return;
903833

904834
FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass,
905-
FillWithNoInit, WarnIfMissingField);
835+
FillWithNoInit);
906836
if (hadError)
907837
return;
908838

@@ -1017,6 +947,13 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
1017947
}
1018948
}
1019949

950+
static bool hasAnyDesignatedInits(const InitListExpr *IL) {
951+
for (const Stmt *Init : *IL)
952+
if (isa_and_nonnull<DesignatedInitExpr>(Init))
953+
return true;
954+
return false;
955+
}
956+
1020957
InitListChecker::InitListChecker(
1021958
Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
1022959
bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
@@ -2288,8 +2225,12 @@ void InitListChecker::CheckStructUnionTypes(
22882225
size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) {
22892226
return isa<FieldDecl>(D) || isa<RecordDecl>(D);
22902227
});
2228+
bool CheckForMissingFields =
2229+
!IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts());
22912230
bool HasDesignatedInit = false;
22922231

2232+
llvm::SmallPtrSet<FieldDecl *, 4> InitializedFields;
2233+
22932234
while (Index < IList->getNumInits()) {
22942235
Expr *Init = IList->getInit(Index);
22952236
SourceLocation InitLoc = Init->getBeginLoc();
@@ -2313,17 +2254,24 @@ void InitListChecker::CheckStructUnionTypes(
23132254

23142255
// Find the field named by the designated initializer.
23152256
DesignatedInitExpr::Designator *D = DIE->getDesignator(0);
2316-
if (!VerifyOnly && D->isFieldDesignator() && !DesignatedInitFailed) {
2257+
if (!VerifyOnly && D->isFieldDesignator()) {
23172258
FieldDecl *F = D->getFieldDecl();
2318-
QualType ET = SemaRef.Context.getBaseElementType(F->getType());
2319-
if (checkDestructorReference(ET, InitLoc, SemaRef)) {
2320-
hadError = true;
2321-
return;
2259+
InitializedFields.insert(F);
2260+
if (!DesignatedInitFailed) {
2261+
QualType ET = SemaRef.Context.getBaseElementType(F->getType());
2262+
if (checkDestructorReference(ET, InitLoc, SemaRef)) {
2263+
hadError = true;
2264+
return;
2265+
}
23222266
}
23232267
}
23242268

23252269
InitializedSomething = true;
23262270

2271+
// Disable check for missing fields when designators are used.
2272+
// This matches gcc behaviour.
2273+
if (!SemaRef.getLangOpts().CPlusPlus)
2274+
CheckForMissingFields = false;
23272275
continue;
23282276
}
23292277

@@ -2402,6 +2350,7 @@ void InitListChecker::CheckStructUnionTypes(
24022350
CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
24032351
StructuredList, StructuredIndex);
24042352
InitializedSomething = true;
2353+
InitializedFields.insert(*Field);
24052354

24062355
if (RD->isUnion() && StructuredList) {
24072356
// Initialize the first field within the union.
@@ -2411,6 +2360,28 @@ void InitListChecker::CheckStructUnionTypes(
24112360
++Field;
24122361
}
24132362

2363+
// Emit warnings for missing struct field initializers.
2364+
if (!VerifyOnly && InitializedSomething && CheckForMissingFields &&
2365+
!RD->isUnion()) {
2366+
// It is possible we have one or more unnamed bitfields remaining.
2367+
// Find first (if any) named field and emit warning.
2368+
for (RecordDecl::field_iterator it = HasDesignatedInit ? RD->field_begin()
2369+
: Field,
2370+
end = RD->field_end();
2371+
it != end; ++it) {
2372+
if (HasDesignatedInit && InitializedFields.count(*it))
2373+
continue;
2374+
2375+
if (!it->isUnnamedBitfield() && !it->hasInClassInitializer() &&
2376+
!it->getType()->isIncompleteArrayType()) {
2377+
SemaRef.Diag(IList->getSourceRange().getEnd(),
2378+
diag::warn_missing_field_initializers)
2379+
<< *it;
2380+
break;
2381+
}
2382+
}
2383+
}
2384+
24142385
// Check that any remaining fields can be value-initialized if we're not
24152386
// building a structured list. (If we are, we'll check this later.)
24162387
if (!StructuredList && Field != FieldEnd && !RD->isUnion() &&

clang/test/Sema/missing-field-initializers.c

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct Foo bar1[] = {
1818
1, 2,
1919
1, 2,
2020
1
21-
}; // expected-warning@-1 {{missing field 'b' initializer}}
21+
}; // expected-warning {{missing field 'b' initializer}}
2222

2323
struct Foo bar2[] = { {}, {}, {} };
2424

@@ -61,26 +61,3 @@ struct S {
6161
// f1, now we no longer issue that warning (note, this code is still unsafe
6262
// because of the buffer overrun).
6363
struct S s = {1, {1, 2}};
64-
65-
struct S1 {
66-
long int l;
67-
struct { int a, b; } d1;
68-
};
69-
70-
struct S1 s01 = { 1, {1} }; // expected-warning {{missing field 'b' initializer}}
71-
struct S1 s02 = { .d1.a = 1 }; // designator avoids MFI warning
72-
73-
union U1 {
74-
long int l;
75-
struct { int a, b; } d1;
76-
};
77-
78-
union U1 u01 = { 1 };
79-
union U1 u02 = { .d1.a = 1 }; // designator avoids MFI warning
80-
81-
struct S2 {
82-
long int l;
83-
struct { int a, b; struct {int c; } d2; } d1;
84-
};
85-
86-
struct S2 s22 = { .d1.d2.c = 1 }; // designator avoids MFI warning

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

Lines changed: 2 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,pedantic,override,reorder -pedantic-errors
22
// RUN: %clang_cc1 -std=c++17 %s -verify=expected,pedantic,override,reorder -Wno-c++20-designator -pedantic-errors
3-
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,pedantic -Werror=c99-designator -Wno-reorder-init-list -Wno-initializer-overrides -Werror=nested-anon-types -Werror=gnu-anonymous-struct
3+
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,pedantic -Werror=c99-designator -Wno-reorder-init-list -Wno-initializer-overrides
44
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,reorder -Wno-c99-designator -Werror=reorder-init-list -Wno-initializer-overrides
55
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,override -Wno-c99-designator -Wno-reorder-init-list -Werror=initializer-overrides
66
// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected -Wno-c99-designator -Wno-reorder-init-list -Wno-initializer-overrides
@@ -39,7 +39,6 @@ A a1 = {
3939
};
4040
int arr[3] = {[1] = 5}; // pedantic-error {{array designators are a C99 extension}}
4141
B b = {.a.x = 0}; // pedantic-error {{nested designators are a C99 extension}}
42-
// wmissing-warning@-1 {{missing field 'y' initializer}}
4342
A a2 = {
4443
.x = 1, // pedantic-error {{mixture of designated and non-designated initializers in the same initializer list is a C99 extension}}
4544
2 // pedantic-note {{first non-designated initializer is here}}
@@ -61,6 +60,7 @@ B b2 = {.a = 1}; // pedantic-error {{brace elision for designated initializer is
6160
B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}}
6261
B b4 = {.a = 1, 2, 3}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}} expected-error {{excess elements}}
6362
B b5 = {.a = nullptr}; // expected-error {{cannot initialize}}
63+
// wmissing-warning@-1 {{missing field 'y' initializer}}
6464
struct C { int :0, x, :0, y, :0; };
6565
C c = {
6666
.x = 1, // override-note {{previous}}
@@ -247,87 +247,3 @@ void foo() {
247247
//
248248
}
249249
}
250-
251-
namespace GH70384 {
252-
253-
struct A {
254-
int m;
255-
union { int a; float n = 0; };
256-
};
257-
258-
struct B {
259-
int m;
260-
int b;
261-
union { int a ; };
262-
};
263-
264-
union CU {
265-
int a = 1;
266-
double b;
267-
};
268-
269-
struct C {
270-
int a;
271-
union { int b; CU c;};
272-
};
273-
274-
struct CC {
275-
int a;
276-
CU c;
277-
};
278-
279-
void foo() {
280-
A a = A{.m = 0};
281-
A aa = {0};
282-
A aaa = {.a = 7}; // wmissing-warning {{missing field 'm' initializer}}
283-
B b = {.m = 1, .b = 3 }; //wmissing-warning {{missing field 'a' initializer}}
284-
B bb = {1}; // wmissing-warning {{missing field 'b' initializer}}
285-
// wmissing-warning@-1 {{missing field 'a' initializer}}
286-
C c = {.a = 1}; // wmissing-warning {{missing field 'b' initializer}}
287-
CC cc = {.a = 1}; // wmissing-warning {{missing field 'c' initializer}}
288-
}
289-
290-
struct C1 {
291-
int m;
292-
union { float b; union {int n = 1; }; };
293-
// pedantic-error@-1 {{anonymous types declared in an anonymous union are an extension}}
294-
};
295-
296-
struct C2 {
297-
int m;
298-
struct { float b; int n = 1; }; // pedantic-error {{anonymous structs are a GNU extension}}
299-
};
300-
301-
struct C3 {
302-
int m;
303-
struct { float b = 1; union {int a;}; int n = 1; };
304-
// pedantic-error@-1 {{anonymous structs are a GNU extension}}
305-
// pedantic-error@-2 {{anonymous types declared in an anonymous struct are an extension}}
306-
};
307-
308-
C1 c = C1{.m = 1};
309-
C1 cc = C1{.b = 1}; // wmissing-warning {{missing field 'm' initializer}}
310-
C2 c1 = C2{.m = 1}; // wmissing-warning {{missing field 'b' initializer}}
311-
C2 c22 = C2{.m = 1, .b = 1};
312-
C3 c2 = C3{.b = 1}; // wmissing-warning {{missing field 'a' initializer}}
313-
// wmissing-warning@-1 {{missing field 'm' initializer}}
314-
315-
struct C4 {
316-
union {
317-
struct { int n; }; // pedantic-error {{anonymous structs are a GNU extension}}
318-
// pedantic-error@-1 {{anonymous types declared in an anonymous union are an extension}}
319-
int m = 0; };
320-
int z;
321-
};
322-
C4 a = {.z = 1};
323-
324-
struct C5 {
325-
int a;
326-
struct { // pedantic-error {{anonymous structs are a GNU extension}}
327-
int x;
328-
struct { int y = 0; }; // pedantic-error {{anonymous types declared in an anonymous struct are an extension}}
329-
// pedantic-error@-1 {{anonymous structs are a GNU extension}}
330-
};
331-
};
332-
C5 c5 = C5{.a = 0}; //wmissing-warning {{missing field 'x' initializer}}
333-
}

0 commit comments

Comments
 (0)