diff options
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 31 | ||||
-rw-r--r-- | clang/test/Sema/zero-initializer.c | 7 | ||||
-rw-r--r-- | clang/test/SemaCXX/aggregate-initialization.cpp | 32 |
3 files changed, 66 insertions, 4 deletions
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 341e7120fe8..dc7fe1d92b0 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -826,6 +826,34 @@ int InitListChecker::numStructUnionElements(QualType DeclType) { return InitializableMembers - structDecl->hasFlexibleArrayMember(); } +/// Determine whether Entity is an entity for which it is idiomatic to elide +/// the braces in aggregate initialization. +static bool isIdiomaticBraceElisionEntity(const InitializedEntity &Entity) { + // Recursive initialization of the one and only field within an aggregate + // class is considered idiomatic. This case arises in particular for + // initialization of std::array, where the C++ standard suggests the idiom of + // + // std::array<T, N> arr = {1, 2, 3}; + // + // (where std::array is an aggregate struct containing a single array field. + + // FIXME: Should aggregate initialization of a struct with a single + // base class and no members also suppress the warning? + if (Entity.getKind() != InitializedEntity::EK_Member || !Entity.getParent()) + return false; + + auto *ParentRD = + Entity.getParent()->getType()->castAs<RecordType>()->getDecl(); + if (CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(ParentRD)) + if (CXXRD->getNumBases()) + return false; + + auto FieldIt = ParentRD->field_begin(); + assert(FieldIt != ParentRD->field_end() && + "no fields but have initializer for member?"); + return ++FieldIt == ParentRD->field_end(); +} + /// Check whether the range of the initializer \p ParentIList from element /// \p Index onwards can be used to initialize an object of type \p T. Update /// \p Index to indicate how many elements of the list were consumed. @@ -887,7 +915,8 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, // Complain about missing braces. if ((T->isArrayType() || T->isRecordType()) && - !ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts())) { + !ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) && + !isIdiomaticBraceElisionEntity(Entity)) { SemaRef.Diag(StructuredSubobjectInitList->getLocStart(), diag::warn_missing_braces) << StructuredSubobjectInitList->getSourceRange() diff --git a/clang/test/Sema/zero-initializer.c b/clang/test/Sema/zero-initializer.c index 472eed9517f..0ab410d4c6d 100644 --- a/clang/test/Sema/zero-initializer.c +++ b/clang/test/Sema/zero-initializer.c @@ -6,6 +6,7 @@ struct bar { struct foo a; struct foo b; }; struct A { int a; }; struct B { struct A a; }; struct C { struct B b; }; +struct D { struct C c; int n; }; int main(void) { @@ -20,7 +21,8 @@ int main(void) struct bar n = { { 0 }, { 9, 9 } }; // no-warning struct bar o = { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}} struct C p = { 0 }; // no-warning - struct C q = { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{suggest braces around initialization of subobject}} + struct C q = { 9 }; // warning suppressed for struct with single element + struct D r = { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{missing field 'n' initializer}} f = (struct foo ) { 0 }; // no-warning g = (struct foo ) { 9 }; // expected-warning {{missing field 'y' initializer}} h = (struct foo ) { 9, 9 }; // no-warning @@ -32,7 +34,8 @@ int main(void) n = (struct bar) { { 0 }, { 9, 9 } }; // no-warning o = (struct bar) { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}} p = (struct C) { 0 }; // no-warning - q = (struct C) { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{suggest braces around initialization of subobject}} + q = (struct C) { 9 }; // warning suppressed for struct with single element + r = (struct D) { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{missing field 'n' initializer}} return 0; } diff --git a/clang/test/SemaCXX/aggregate-initialization.cpp b/clang/test/SemaCXX/aggregate-initialization.cpp index 7b6abd22acc..514473f9bcb 100644 --- a/clang/test/SemaCXX/aggregate-initialization.cpp +++ b/clang/test/SemaCXX/aggregate-initialization.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s // Verify that using an initializer list for a non-aggregate looks for // constructors.. @@ -150,3 +150,33 @@ namespace ProtectedBaseCtor { // expected-error@-5 {{protected constructor}} // expected-note@-30 {{here}} } + +namespace IdiomaticStdArrayInitDoesNotWarn { +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wmissing-braces" + template<typename T, int N> struct StdArray { + T contents[N]; + }; + StdArray<int, 3> x = {1, 2, 3}; + + template<typename T, int N> struct ArrayAndSomethingElse { + T contents[N]; + int something_else; + }; + ArrayAndSomethingElse<int, 3> y = {1, 2, 3}; // expected-warning {{suggest braces}} + +#if __cplusplus >= 201703L + template<typename T, int N> struct ArrayAndBaseClass : StdArray<int, 3> { + T contents[N]; + }; + ArrayAndBaseClass<int, 3> z = {1, 2, 3}; // expected-warning {{suggest braces}} + + // It's not clear whether we should be warning in this case. If this + // pattern becomes idiomatic, it would be reasonable to suppress the + // warning here too. + template<typename T, int N> struct JustABaseClass : StdArray<T, N> {}; + JustABaseClass<int, 3> w = {1, 2, 3}; // expected-warning {{suggest braces}} +#endif + +#pragma clang diagnostic pop +} |