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.

llvm-svn: 314838
This commit is contained in:
Richard Smith 2017-10-03 20:36:00 +00:00
parent 389b7cedc3
commit 283e2076f6
3 changed files with 66 additions and 4 deletions

View File

@ -826,6 +826,34 @@ int InitListChecker::numStructUnionElements(QualType DeclType) {
return InitializableMembers - structDecl->hasFlexibleArrayMember(); 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 /// 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 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. /// \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. // Complain about missing braces.
if ((T->isArrayType() || T->isRecordType()) && if ((T->isArrayType() || T->isRecordType()) &&
!ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts())) { !ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) &&
!isIdiomaticBraceElisionEntity(Entity)) {
SemaRef.Diag(StructuredSubobjectInitList->getLocStart(), SemaRef.Diag(StructuredSubobjectInitList->getLocStart(),
diag::warn_missing_braces) diag::warn_missing_braces)
<< StructuredSubobjectInitList->getSourceRange() << StructuredSubobjectInitList->getSourceRange()

View File

@ -6,6 +6,7 @@ struct bar { struct foo a; struct foo b; };
struct A { int a; }; struct A { int a; };
struct B { struct A a; }; struct B { struct A a; };
struct C { struct B b; }; struct C { struct B b; };
struct D { struct C c; int n; };
int main(void) int main(void)
{ {
@ -20,7 +21,8 @@ int main(void)
struct bar n = { { 0 }, { 9, 9 } }; // no-warning struct bar n = { { 0 }, { 9, 9 } }; // no-warning
struct bar o = { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}} struct bar o = { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
struct C p = { 0 }; // no-warning 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 f = (struct foo ) { 0 }; // no-warning
g = (struct foo ) { 9 }; // expected-warning {{missing field 'y' initializer}} g = (struct foo ) { 9 }; // expected-warning {{missing field 'y' initializer}}
h = (struct foo ) { 9, 9 }; // no-warning h = (struct foo ) { 9, 9 }; // no-warning
@ -32,7 +34,8 @@ int main(void)
n = (struct bar) { { 0 }, { 9, 9 } }; // no-warning n = (struct bar) { { 0 }, { 9, 9 } }; // no-warning
o = (struct bar) { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}} o = (struct bar) { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
p = (struct C) { 0 }; // no-warning 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; return 0;
} }

View File

@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s // 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++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 // Verify that using an initializer list for a non-aggregate looks for
// constructors.. // constructors..
@ -150,3 +150,33 @@ namespace ProtectedBaseCtor {
// expected-error@-5 {{protected constructor}} // expected-error@-5 {{protected constructor}}
// expected-note@-30 {{here}} // 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
}