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,23 +1007,35 @@ 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;
// Allows elide brace initialization for aggregates with empty base.
if (Entity.getKind() == InitializedEntity::EK_Base) {
auto *ParentRD = auto *ParentRD =
Entity.getParent()->getType()->castAs<RecordType>()->getDecl(); Entity.getParent()->getType()->castAs<RecordType>()->getDecl();
if (CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(ParentRD)) CXXRecordDecl *CXXRD = cast<CXXRecordDecl>(ParentRD);
if (CXXRD->getNumBases()) return CXXRD->getNumBases() == 1 && CXXRD->field_empty();
return false; }
// Allow brace elision if the only subobject is a field.
if (Entity.getKind() == InitializedEntity::EK_Member) {
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(); auto FieldIt = ParentRD->field_begin();
assert(FieldIt != ParentRD->field_end() && assert(FieldIt != ParentRD->field_end() &&
"no fields but have initializer for member?"); "no fields but have initializer for member?");
return ++FieldIt == ParentRD->field_end(); 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
/// \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.

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