diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 035eaae58965a..d6459fd9d7875 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -465,8 +465,7 @@ class InitListChecker { void FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, InitListExpr *ILE, bool &RequiresSecondPass, - bool FillWithNoInit = false, - bool WarnIfMissing = false); + bool FillWithNoInit = false); void FillInEmptyInitializations(const InitializedEntity &Entity, InitListExpr *ILE, bool &RequiresSecondPass, InitListExpr *OuterILE, unsigned OuterIndex, @@ -655,16 +654,11 @@ void InitListChecker::FillInEmptyInitForBase( } } -static bool hasAnyDesignatedInits(const InitListExpr *IL) { - return llvm::any_of(*IL, [=](const Stmt *Init) { - return isa_and_nonnull(Init); - }); -} - -void InitListChecker::FillInEmptyInitForField( - unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, - InitListExpr *ILE, bool &RequiresSecondPass, bool FillWithNoInit, - bool WarnIfMissing) { +void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, + const InitializedEntity &ParentEntity, + InitListExpr *ILE, + bool &RequiresSecondPass, + bool FillWithNoInit) { SourceLocation Loc = ILE->getEndLoc(); unsigned NumInits = ILE->getNumInits(); InitializedEntity MemberEntity @@ -732,52 +726,15 @@ void InitListChecker::FillInEmptyInitForField( if (hadError || VerifyOnly) { // Do nothing - } else { - if (WarnIfMissing) { - auto CheckAnonMember = [&](const FieldDecl *FD, - auto &&CheckAnonMember) -> FieldDecl * { - FieldDecl *Uninitialized = nullptr; - RecordDecl *RD = FD->getType()->getAsRecordDecl(); - assert(RD && "Not anonymous member checked?"); - for (auto *F : RD->fields()) { - FieldDecl *UninitializedFieldInF = nullptr; - if (F->isAnonymousStructOrUnion()) - UninitializedFieldInF = CheckAnonMember(F, CheckAnonMember); - else if (!F->isUnnamedBitfield() && - !F->getType()->isIncompleteArrayType() && - !F->hasInClassInitializer()) - UninitializedFieldInF = F; - - if (RD->isUnion() && !UninitializedFieldInF) - return nullptr; - if (!Uninitialized) - Uninitialized = UninitializedFieldInF; - } - return Uninitialized; - }; - - FieldDecl *FieldToDiagnose = nullptr; - if (Field->isAnonymousStructOrUnion()) - FieldToDiagnose = CheckAnonMember(Field, CheckAnonMember); - else if (!Field->isUnnamedBitfield() && - !Field->getType()->isIncompleteArrayType()) - FieldToDiagnose = Field; - - if (FieldToDiagnose) - SemaRef.Diag(Loc, diag::warn_missing_field_initializers) - << FieldToDiagnose; - } - - if (Init < NumInits) { - ILE->setInit(Init, MemberInit.getAs()); - } else if (!isa(MemberInit.get())) { - // Empty initialization requires a constructor call, so - // extend the initializer list to include the constructor - // call and make a note that we'll need to take another pass - // through the initializer list. - ILE->updateInit(SemaRef.Context, Init, MemberInit.getAs()); - RequiresSecondPass = true; - } + } else if (Init < NumInits) { + ILE->setInit(Init, MemberInit.getAs()); + } else if (!isa(MemberInit.get())) { + // Empty initialization requires a constructor call, so + // extend the initializer list to include the constructor + // call and make a note that we'll need to take another pass + // through the initializer list. + ILE->updateInit(SemaRef.Context, Init, MemberInit.getAs()); + RequiresSecondPass = true; } } else if (InitListExpr *InnerILE = dyn_cast(ILE->getInit(Init))) { @@ -845,36 +802,9 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, } } } else { - InitListExpr *SForm = - ILE->isSyntacticForm() ? ILE : ILE->getSyntacticForm(); // 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. - - // Some checks that are required for missing fields warning are bound to - // how many elements the initializer list originally was provided; perform - // them before the list is expanded. - bool WarnIfMissingField = - !SForm->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) && - ILE->getNumInits(); - - // Disable check for missing fields when designators are used in C to - // match gcc behaviour. - // FIXME: Should we emulate possible gcc warning bug? - WarnIfMissingField &= - SemaRef.getLangOpts().CPlusPlus || !hasAnyDesignatedInits(SForm); - - if (OuterILE) { - // When nested designators are present, there might be two nested init - // lists created and only outer will contain designated initializer - // expression, so check outer list as well. - InitListExpr *OuterSForm = OuterILE->isSyntacticForm() - ? OuterILE - : OuterILE->getSyntacticForm(); - WarnIfMissingField &= SemaRef.getLangOpts().CPlusPlus || - !hasAnyDesignatedInits(OuterSForm); - } - unsigned NumElems = numStructUnionElements(ILE->getType()); if (!RDecl->isUnion() && RDecl->hasFlexibleArrayMember()) ++NumElems; @@ -902,7 +832,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, return; FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass, - FillWithNoInit, WarnIfMissingField); + FillWithNoInit); if (hadError) return; @@ -1017,6 +947,13 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, } } +static bool hasAnyDesignatedInits(const InitListExpr *IL) { + for (const Stmt *Init : *IL) + if (isa_and_nonnull(Init)) + return true; + return false; +} + InitListChecker::InitListChecker( Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution, @@ -2288,8 +2225,12 @@ void InitListChecker::CheckStructUnionTypes( size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) { return isa(D) || isa(D); }); + bool CheckForMissingFields = + !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); bool HasDesignatedInit = false; + llvm::SmallPtrSet InitializedFields; + while (Index < IList->getNumInits()) { Expr *Init = IList->getInit(Index); SourceLocation InitLoc = Init->getBeginLoc(); @@ -2313,17 +2254,24 @@ void InitListChecker::CheckStructUnionTypes( // Find the field named by the designated initializer. DesignatedInitExpr::Designator *D = DIE->getDesignator(0); - if (!VerifyOnly && D->isFieldDesignator() && !DesignatedInitFailed) { + if (!VerifyOnly && D->isFieldDesignator()) { FieldDecl *F = D->getFieldDecl(); - QualType ET = SemaRef.Context.getBaseElementType(F->getType()); - if (checkDestructorReference(ET, InitLoc, SemaRef)) { - hadError = true; - return; + InitializedFields.insert(F); + if (!DesignatedInitFailed) { + QualType ET = SemaRef.Context.getBaseElementType(F->getType()); + if (checkDestructorReference(ET, InitLoc, SemaRef)) { + hadError = true; + return; + } } } InitializedSomething = true; + // Disable check for missing fields when designators are used. + // This matches gcc behaviour. + if (!SemaRef.getLangOpts().CPlusPlus) + CheckForMissingFields = false; continue; } @@ -2402,6 +2350,7 @@ void InitListChecker::CheckStructUnionTypes( CheckSubElementType(MemberEntity, IList, Field->getType(), Index, StructuredList, StructuredIndex); InitializedSomething = true; + InitializedFields.insert(*Field); if (RD->isUnion() && StructuredList) { // Initialize the first field within the union. @@ -2411,6 +2360,28 @@ void InitListChecker::CheckStructUnionTypes( ++Field; } + // Emit warnings for missing struct field initializers. + if (!VerifyOnly && InitializedSomething && CheckForMissingFields && + !RD->isUnion()) { + // It is possible we have one or more unnamed bitfields remaining. + // Find first (if any) named field and emit warning. + for (RecordDecl::field_iterator it = HasDesignatedInit ? RD->field_begin() + : Field, + end = RD->field_end(); + it != end; ++it) { + if (HasDesignatedInit && InitializedFields.count(*it)) + continue; + + if (!it->isUnnamedBitfield() && !it->hasInClassInitializer() && + !it->getType()->isIncompleteArrayType()) { + SemaRef.Diag(IList->getSourceRange().getEnd(), + diag::warn_missing_field_initializers) + << *it; + break; + } + } + } + // Check that any remaining fields can be value-initialized if we're not // building a structured list. (If we are, we'll check this later.) if (!StructuredList && Field != FieldEnd && !RD->isUnion() && diff --git a/clang/test/Sema/missing-field-initializers.c b/clang/test/Sema/missing-field-initializers.c index 8dc8288ad92e6..1e65b2d62e1ab 100644 --- a/clang/test/Sema/missing-field-initializers.c +++ b/clang/test/Sema/missing-field-initializers.c @@ -18,7 +18,7 @@ struct Foo bar1[] = { 1, 2, 1, 2, 1 -}; // expected-warning@-1 {{missing field 'b' initializer}} +}; // expected-warning {{missing field 'b' initializer}} struct Foo bar2[] = { {}, {}, {} }; @@ -61,26 +61,3 @@ struct S { // f1, now we no longer issue that warning (note, this code is still unsafe // because of the buffer overrun). struct S s = {1, {1, 2}}; - -struct S1 { - long int l; - struct { int a, b; } d1; -}; - -struct S1 s01 = { 1, {1} }; // expected-warning {{missing field 'b' initializer}} -struct S1 s02 = { .d1.a = 1 }; // designator avoids MFI warning - -union U1 { - long int l; - struct { int a, b; } d1; -}; - -union U1 u01 = { 1 }; -union U1 u02 = { .d1.a = 1 }; // designator avoids MFI warning - -struct S2 { - long int l; - struct { int a, b; struct {int c; } d2; } d1; -}; - -struct S2 s22 = { .d1.d2.c = 1 }; // designator avoids MFI warning diff --git a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp index 0d977e07ed034..510ace58c35a6 100644 --- a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp +++ b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,pedantic,override,reorder -pedantic-errors // RUN: %clang_cc1 -std=c++17 %s -verify=expected,pedantic,override,reorder -Wno-c++20-designator -pedantic-errors -// 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 +// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,pedantic -Werror=c99-designator -Wno-reorder-init-list -Wno-initializer-overrides // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,reorder -Wno-c99-designator -Werror=reorder-init-list -Wno-initializer-overrides // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,override -Wno-c99-designator -Wno-reorder-init-list -Werror=initializer-overrides // 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 = { }; int arr[3] = {[1] = 5}; // pedantic-error {{array designators are a C99 extension}} B b = {.a.x = 0}; // pedantic-error {{nested designators are a C99 extension}} - // wmissing-warning@-1 {{missing field 'y' initializer}} A a2 = { .x = 1, // pedantic-error {{mixture of designated and non-designated initializers in the same initializer list is a C99 extension}} 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 B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}} 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}} B b5 = {.a = nullptr}; // expected-error {{cannot initialize}} + // wmissing-warning@-1 {{missing field 'y' initializer}} struct C { int :0, x, :0, y, :0; }; C c = { .x = 1, // override-note {{previous}} @@ -247,87 +247,3 @@ void foo() { // } } - -namespace GH70384 { - -struct A { - int m; - union { int a; float n = 0; }; -}; - -struct B { - int m; - int b; - union { int a ; }; -}; - -union CU { - int a = 1; - double b; -}; - -struct C { - int a; - union { int b; CU c;}; -}; - -struct CC { - int a; - CU c; -}; - -void foo() { - A a = A{.m = 0}; - A aa = {0}; - A aaa = {.a = 7}; // wmissing-warning {{missing field 'm' initializer}} - B b = {.m = 1, .b = 3 }; //wmissing-warning {{missing field 'a' initializer}} - B bb = {1}; // wmissing-warning {{missing field 'b' initializer}} - // wmissing-warning@-1 {{missing field 'a' initializer}} - C c = {.a = 1}; // wmissing-warning {{missing field 'b' initializer}} - CC cc = {.a = 1}; // wmissing-warning {{missing field 'c' initializer}} -} - -struct C1 { - int m; - union { float b; union {int n = 1; }; }; - // pedantic-error@-1 {{anonymous types declared in an anonymous union are an extension}} -}; - -struct C2 { - int m; - struct { float b; int n = 1; }; // pedantic-error {{anonymous structs are a GNU extension}} -}; - -struct C3 { - int m; - struct { float b = 1; union {int a;}; int n = 1; }; - // pedantic-error@-1 {{anonymous structs are a GNU extension}} - // pedantic-error@-2 {{anonymous types declared in an anonymous struct are an extension}} -}; - -C1 c = C1{.m = 1}; -C1 cc = C1{.b = 1}; // wmissing-warning {{missing field 'm' initializer}} -C2 c1 = C2{.m = 1}; // wmissing-warning {{missing field 'b' initializer}} -C2 c22 = C2{.m = 1, .b = 1}; -C3 c2 = C3{.b = 1}; // wmissing-warning {{missing field 'a' initializer}} - // wmissing-warning@-1 {{missing field 'm' initializer}} - -struct C4 { - union { - struct { int n; }; // pedantic-error {{anonymous structs are a GNU extension}} - // pedantic-error@-1 {{anonymous types declared in an anonymous union are an extension}} - int m = 0; }; - int z; -}; -C4 a = {.z = 1}; - -struct C5 { - int a; - struct { // pedantic-error {{anonymous structs are a GNU extension}} - int x; - struct { int y = 0; }; // pedantic-error {{anonymous types declared in an anonymous struct are an extension}} - // pedantic-error@-1 {{anonymous structs are a GNU extension}} - }; -}; -C5 c5 = C5{.a = 0}; //wmissing-warning {{missing field 'x' initializer}} -}