Skip to content

Commit

Permalink
PR49020: Diagnose brace elision in designated initializers in C++.
Browse files Browse the repository at this point in the history
This is a corner of the differences between C99 designators and C++20
designators that we'd previously overlooked. As with other such cases,
this continues to be permitted as an extension and allowed by default,
behind the -Wc99-designators warning flag, except in cases where it
leads to a conformance difference (such as in overload resolution and in
a SFINAE context).
  • Loading branch information
zygoloid committed Feb 3, 2021
1 parent 1a13ee1 commit b15cbaf
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ def ext_designated_init_reordered : ExtWarn<
SFINAEFailure;
def note_previous_field_init : Note<
"previous initialization for field %0 is here">;
def ext_designated_init_brace_elision : ExtWarn<
"brace elision for designated initializer is a C99 extension">,
InGroup<C99Designator>, SFINAEFailure;

// Declarations.
def ext_plain_complex : ExtWarn<
Expand Down
24 changes: 20 additions & 4 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ class InitListChecker {
InitListExpr *IList, QualType ElemType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
unsigned &StructuredIndex,
bool DirectlyDesignated = false);
void CheckComplexType(const InitializedEntity &Entity,
InitListExpr *IList, QualType DeclType,
unsigned &Index,
Expand Down Expand Up @@ -1326,7 +1327,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
QualType ElemType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex) {
unsigned &StructuredIndex,
bool DirectlyDesignated) {
Expr *expr = IList->getInit(Index);

if (ElemType->isReferenceType())
Expand Down Expand Up @@ -1462,6 +1464,20 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
CheckImplicitInitList(Entity, IList, ElemType, Index, StructuredList,
StructuredIndex);
++StructuredIndex;

// In C++20, brace elision is not permitted for a designated initializer.
if (DirectlyDesignated && SemaRef.getLangOpts().CPlusPlus && !hadError) {
if (InOverloadResolution)
hadError = true;
if (!VerifyOnly) {
SemaRef.Diag(expr->getBeginLoc(),
diag::ext_designated_init_brace_elision)
<< expr->getSourceRange()
<< FixItHint::CreateInsertion(expr->getBeginLoc(), "{")
<< FixItHint::CreateInsertion(
SemaRef.getLocForEndOfToken(expr->getEndLoc()), "}");
}
}
} else {
if (!VerifyOnly) {
// We cannot initialize this element, so let PerformCopyInitialization
Expand Down Expand Up @@ -2413,8 +2429,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
unsigned OldIndex = Index;
IList->setInit(OldIndex, DIE->getInit());

CheckSubElementType(Entity, IList, CurrentObjectType, Index,
StructuredList, StructuredIndex);
CheckSubElementType(Entity, IList, CurrentObjectType, Index, StructuredList,
StructuredIndex, /*DirectlyDesignated=*/true);

// Restore the designated initializer expression in the syntactic
// form of the initializer list.
Expand Down
27 changes: 27 additions & 0 deletions clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ A a5 = {
.y = 1, // override-note {{previous}}
.y = 1 // override-error {{overrides prior initialization}}
};
B b2 = {.a = 1}; // pedantic-error {{brace elision for designated initializer is a C99 extension}}
B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}}
B b4 = {.a = 1, 2, 3}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}} expected-error {{excess elements}}
B b5 = {.a = nullptr}; // expected-error {{cannot initialize}}
struct C { int :0, x, :0, y, :0; };
C c = {
.x = 1, // override-note {{previous}}
Expand Down Expand Up @@ -112,6 +116,15 @@ namespace overload_resolution {
void k() {
j({.x = 1, .y = 2}); // expected-error {{ambiguous}}
}

struct E { A a; };
struct F { int a; };
void l(E e); // expected-note {{candidate}}
int &l(F f); // expected-note {{candidate}}
void m() {
int &r = l({.a = 0}); // ok, l(E) is not viable
int &s = l({.a = {0}}); // expected-error {{ambiguous}}
}
}

namespace deduction {
Expand All @@ -128,4 +141,18 @@ namespace deduction {
void i() {
h<A, C>(); // ok, selects C overload by SFINAE
}

struct D { int n; };
struct E { D n; };
template<typename T, typename U> void j1(decltype(T{.n = 0}));
template<typename T, typename U> void j1(decltype(U{.n = 0})) = delete;
template<typename T, typename U> void j2(decltype(T{.n = {0}})); // expected-note {{candidate}}
template<typename T, typename U> void j2(decltype(U{.n = {0}})); // expected-note {{candidate}}
template<typename T, typename U> void j3(decltype(T{.n = {{0}}})) = delete;
template<typename T, typename U> void j3(decltype(U{.n = {{0}}}));
void k() {
j1<D, E>({}); // ok, selects D overload by SFINAE (too few braces for E)
j2<D, E>({}); // expected-error {{ambiguous}}
j3<D, E>({}); // ok, selects E overload by SFINAE (too many braces for D)
}
}

0 comments on commit b15cbaf

Please sign in to comment.