Skip to content

Commit a01307a

Browse files
authored
[clang] Fix false positive -Wmissing-field-initializer for anonymous unions (#70829)
Normally warning is not reported when a field has default initializer. Do so for anonymous unions with default initializers as well. No release note since it is a regression in clang 18. Fixes #70384
1 parent b003fed commit a01307a

File tree

3 files changed

+167
-65
lines changed

3 files changed

+167
-65
lines changed

clang/lib/Sema/SemaInit.cpp

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

657-
void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
658-
const InitializedEntity &ParentEntity,
659-
InitListExpr *ILE,
660-
bool &RequiresSecondPass,
661-
bool FillWithNoInit) {
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) {
662668
SourceLocation Loc = ILE->getEndLoc();
663669
unsigned NumInits = ILE->getNumInits();
664670
InitializedEntity MemberEntity
@@ -726,15 +732,52 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
726732

727733
if (hadError || VerifyOnly) {
728734
// Do nothing
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;
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+
}
738781
}
739782
} else if (InitListExpr *InnerILE
740783
= dyn_cast<InitListExpr>(ILE->getInit(Init))) {
@@ -802,9 +845,25 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
802845
}
803846
}
804847
} else {
848+
InitListExpr *SForm =
849+
ILE->isSyntacticForm() ? ILE : ILE->getSyntacticForm();
805850
// The fields beyond ILE->getNumInits() are default initialized, so in
806851
// order to leave them uninitialized, the ILE is expanded and the extra
807852
// 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+
808867
unsigned NumElems = numStructUnionElements(ILE->getType());
809868
if (!RDecl->isUnion() && RDecl->hasFlexibleArrayMember())
810869
++NumElems;
@@ -832,7 +891,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
832891
return;
833892

834893
FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass,
835-
FillWithNoInit);
894+
FillWithNoInit, WarnIfMissingField);
836895
if (hadError)
837896
return;
838897

@@ -947,13 +1006,6 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
9471006
}
9481007
}
9491008

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-
9571009
InitListChecker::InitListChecker(
9581010
Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
9591011
bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
@@ -2225,12 +2277,8 @@ void InitListChecker::CheckStructUnionTypes(
22252277
size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) {
22262278
return isa<FieldDecl>(D) || isa<RecordDecl>(D);
22272279
});
2228-
bool CheckForMissingFields =
2229-
!IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts());
22302280
bool HasDesignatedInit = false;
22312281

2232-
llvm::SmallPtrSet<FieldDecl *, 4> InitializedFields;
2233-
22342282
while (Index < IList->getNumInits()) {
22352283
Expr *Init = IList->getInit(Index);
22362284
SourceLocation InitLoc = Init->getBeginLoc();
@@ -2254,24 +2302,17 @@ void InitListChecker::CheckStructUnionTypes(
22542302

22552303
// Find the field named by the designated initializer.
22562304
DesignatedInitExpr::Designator *D = DIE->getDesignator(0);
2257-
if (!VerifyOnly && D->isFieldDesignator()) {
2305+
if (!VerifyOnly && D->isFieldDesignator() && !DesignatedInitFailed) {
22582306
FieldDecl *F = D->getFieldDecl();
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-
}
2307+
QualType ET = SemaRef.Context.getBaseElementType(F->getType());
2308+
if (checkDestructorReference(ET, InitLoc, SemaRef)) {
2309+
hadError = true;
2310+
return;
22662311
}
22672312
}
22682313

22692314
InitializedSomething = true;
22702315

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

@@ -2350,7 +2391,6 @@ void InitListChecker::CheckStructUnionTypes(
23502391
CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
23512392
StructuredList, StructuredIndex);
23522393
InitializedSomething = true;
2353-
InitializedFields.insert(*Field);
23542394

23552395
if (RD->isUnion() && StructuredList) {
23562396
// Initialize the first field within the union.
@@ -2360,28 +2400,6 @@ void InitListChecker::CheckStructUnionTypes(
23602400
++Field;
23612401
}
23622402

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-
23852403
// Check that any remaining fields can be value-initialized if we're not
23862404
// building a structured list. (If we are, we'll check this later.)
23872405
if (!StructuredList && Field != FieldEnd && !RD->isUnion() &&

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

Lines changed: 1 addition & 1 deletion
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 {{missing field 'b' initializer}}
21+
}; // expected-warning@-1 {{missing field 'b' initializer}}
2222

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

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

Lines changed: 86 additions & 2 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
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
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,6 +39,7 @@ 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}}
4243
A a2 = {
4344
.x = 1, // pedantic-error {{mixture of designated and non-designated initializers in the same initializer list is a C99 extension}}
4445
2 // pedantic-note {{first non-designated initializer is here}}
@@ -60,7 +61,6 @@ B b2 = {.a = 1}; // pedantic-error {{brace elision for designated initializer is
6061
B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}}
6162
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}}
6263
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,3 +247,87 @@ 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)