Author: rsmith
Date: Tue Oct  3 13:36:00 2017
New Revision: 314838

URL: http://llvm.org/viewvc/llvm-project?rev=314838&view=rev
Log:
Suppress -Wmissing-braces warning when aggregate-initializing a struct with a 
single field that is itself an aggregate.

In C++, such initialization of std::array<T, N> types is guaranteed to work by
the standard, is completely idiomatic, and the "suggested" alternative from
Clang was technically invalid.

Modified:
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/test/Sema/zero-initializer.c
    cfe/trunk/test/SemaCXX/aggregate-initialization.cpp

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=314838&r1=314837&r2=314838&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Tue Oct  3 13:36:00 2017
@@ -826,6 +826,34 @@ int InitListChecker::numStructUnionEleme
   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::CheckImplicitInitL
 
     // 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()

Modified: cfe/trunk/test/Sema/zero-initializer.c
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/zero-initializer.c?rev=314838&r1=314837&r2=314838&view=diff
==============================================================================
--- cfe/trunk/test/Sema/zero-initializer.c (original)
+++ cfe/trunk/test/Sema/zero-initializer.c Tue Oct  3 13:36:00 2017
@@ -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;
 }

Modified: cfe/trunk/test/SemaCXX/aggregate-initialization.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/aggregate-initialization.cpp?rev=314838&r1=314837&r2=314838&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/aggregate-initialization.cpp (original)
+++ cfe/trunk/test/SemaCXX/aggregate-initialization.cpp Tue Oct  3 13:36:00 2017
@@ -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
+}


_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to