Remove warning "suggest braces" for aggregate initialization of an empty class with an aggregate base class.

I recently ran into issues with aggregates and inheritance, I'm using
it for creating a type-safe library where most of the types are build
over "tagged" std::array. After bit of cleaning and enabling -Wall
-Wextra -pedantic I noticed clang only in my pipeline gives me warning.
After a bit of focusing on it I found it's not helpful, and contemplate
disabling the warning all together. After a discussion with other
library authors I found it's bothering more people and decided to fix
it.

Removes this warning:

template<typename T, int N> struct StdArray {
    T contents[N];
  };

template<typename T, int N> struct AggregateAndEmpty : StdArray<T,N> { };

AggregateAndEmpty<int, 3> p = {1, 2, 3}; // <-- warning here about omitted braces
This commit is contained in:
Hana Dusíková 2021-04-13 15:45:09 -04:00 committed by Aaron Ballman
parent 5b15fe9334
commit 64c24f493e
2 changed files with 37 additions and 16 deletions

View File

@ -1007,21 +1007,33 @@ static bool isIdiomaticBraceElisionEntity(const InitializedEntity &Entity) {
// //
// (where std::array is an aggregate struct containing a single array field. // (where std::array is an aggregate struct containing a single array field.
// FIXME: Should aggregate initialization of a struct with a single if (!Entity.getParent())
// base class and no members also suppress the warning?
if (Entity.getKind() != InitializedEntity::EK_Member || !Entity.getParent())
return false; return false;
auto *ParentRD = // Allows elide brace initialization for aggregates with empty base.
Entity.getParent()->getType()->castAs<RecordType>()->getDecl(); if (Entity.getKind() == InitializedEntity::EK_Base) {
if (CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(ParentRD)) auto *ParentRD =
if (CXXRD->getNumBases()) Entity.getParent()->getType()->castAs<RecordType>()->getDecl();
return false; CXXRecordDecl *CXXRD = cast<CXXRecordDecl>(ParentRD);
return CXXRD->getNumBases() == 1 && CXXRD->field_empty();
}
auto FieldIt = ParentRD->field_begin(); // Allow brace elision if the only subobject is a field.
assert(FieldIt != ParentRD->field_end() && if (Entity.getKind() == InitializedEntity::EK_Member) {
"no fields but have initializer for member?"); auto *ParentRD =
return ++FieldIt == ParentRD->field_end(); 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();
}
return false;
} }
/// Check whether the range of the initializer \p ParentIList from element /// Check whether the range of the initializer \p ParentIList from element

View File

@ -172,11 +172,20 @@ namespace IdiomaticStdArrayInitDoesNotWarn {
}; };
ArrayAndBaseClass<int, 3> z = {1, 2, 3}; // expected-warning {{suggest braces}} 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 // This pattern is used for tagged aggregates and must not warn
// pattern becomes idiomatic, it would be reasonable to suppress the
// warning here too.
template<typename T, int N> struct JustABaseClass : StdArray<T, N> {}; template<typename T, int N> struct JustABaseClass : StdArray<T, N> {};
JustABaseClass<int, 3> w = {1, 2, 3}; // expected-warning {{suggest braces}} JustABaseClass<int, 3> w = {1, 2, 3};
// but this should be also ok
JustABaseClass<int, 3> v = {{1, 2, 3}};
template <typename T, int N> struct OnionBaseClass : JustABaseClass<T, N> {};
OnionBaseClass<int, 3> u = {1, 2, 3};
OnionBaseClass<int, 3> t = {{{1, 2, 3}}};
struct EmptyBase {};
template <typename T, int N> struct AggregateAndEmpty : StdArray<T, N>, EmptyBase {};
AggregateAndEmpty<int, 3> p = {1, 2, 3}; // expected-warning {{suggest braces}}
#endif #endif
#pragma clang diagnostic pop #pragma clang diagnostic pop