Skip to content

Commit b15cbaf

Browse files
committed
PR49020: Diagnose brace elision in designated initializers in C++.
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).
1 parent 1a13ee1 commit b15cbaf

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ def ext_designated_init_reordered : ExtWarn<
214214
SFINAEFailure;
215215
def note_previous_field_init : Note<
216216
"previous initialization for field %0 is here">;
217+
def ext_designated_init_brace_elision : ExtWarn<
218+
"brace elision for designated initializer is a C99 extension">,
219+
InGroup<C99Designator>, SFINAEFailure;
217220

218221
// Declarations.
219222
def ext_plain_complex : ExtWarn<

clang/lib/Sema/SemaInit.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,8 @@ class InitListChecker {
315315
InitListExpr *IList, QualType ElemType,
316316
unsigned &Index,
317317
InitListExpr *StructuredList,
318-
unsigned &StructuredIndex);
318+
unsigned &StructuredIndex,
319+
bool DirectlyDesignated = false);
319320
void CheckComplexType(const InitializedEntity &Entity,
320321
InitListExpr *IList, QualType DeclType,
321322
unsigned &Index,
@@ -1326,7 +1327,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
13261327
QualType ElemType,
13271328
unsigned &Index,
13281329
InitListExpr *StructuredList,
1329-
unsigned &StructuredIndex) {
1330+
unsigned &StructuredIndex,
1331+
bool DirectlyDesignated) {
13301332
Expr *expr = IList->getInit(Index);
13311333

13321334
if (ElemType->isReferenceType())
@@ -1462,6 +1464,20 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
14621464
CheckImplicitInitList(Entity, IList, ElemType, Index, StructuredList,
14631465
StructuredIndex);
14641466
++StructuredIndex;
1467+
1468+
// In C++20, brace elision is not permitted for a designated initializer.
1469+
if (DirectlyDesignated && SemaRef.getLangOpts().CPlusPlus && !hadError) {
1470+
if (InOverloadResolution)
1471+
hadError = true;
1472+
if (!VerifyOnly) {
1473+
SemaRef.Diag(expr->getBeginLoc(),
1474+
diag::ext_designated_init_brace_elision)
1475+
<< expr->getSourceRange()
1476+
<< FixItHint::CreateInsertion(expr->getBeginLoc(), "{")
1477+
<< FixItHint::CreateInsertion(
1478+
SemaRef.getLocForEndOfToken(expr->getEndLoc()), "}");
1479+
}
1480+
}
14651481
} else {
14661482
if (!VerifyOnly) {
14671483
// We cannot initialize this element, so let PerformCopyInitialization
@@ -2413,8 +2429,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
24132429
unsigned OldIndex = Index;
24142430
IList->setInit(OldIndex, DIE->getInit());
24152431

2416-
CheckSubElementType(Entity, IList, CurrentObjectType, Index,
2417-
StructuredList, StructuredIndex);
2432+
CheckSubElementType(Entity, IList, CurrentObjectType, Index, StructuredList,
2433+
StructuredIndex, /*DirectlyDesignated=*/true);
24182434

24192435
// Restore the designated initializer expression in the syntactic
24202436
// form of the initializer list.

clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ A a5 = {
5454
.y = 1, // override-note {{previous}}
5555
.y = 1 // override-error {{overrides prior initialization}}
5656
};
57+
B b2 = {.a = 1}; // pedantic-error {{brace elision for designated initializer is a C99 extension}}
58+
B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}}
59+
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}}
60+
B b5 = {.a = nullptr}; // expected-error {{cannot initialize}}
5761
struct C { int :0, x, :0, y, :0; };
5862
C c = {
5963
.x = 1, // override-note {{previous}}
@@ -112,6 +116,15 @@ namespace overload_resolution {
112116
void k() {
113117
j({.x = 1, .y = 2}); // expected-error {{ambiguous}}
114118
}
119+
120+
struct E { A a; };
121+
struct F { int a; };
122+
void l(E e); // expected-note {{candidate}}
123+
int &l(F f); // expected-note {{candidate}}
124+
void m() {
125+
int &r = l({.a = 0}); // ok, l(E) is not viable
126+
int &s = l({.a = {0}}); // expected-error {{ambiguous}}
127+
}
115128
}
116129

117130
namespace deduction {
@@ -128,4 +141,18 @@ namespace deduction {
128141
void i() {
129142
h<A, C>(); // ok, selects C overload by SFINAE
130143
}
144+
145+
struct D { int n; };
146+
struct E { D n; };
147+
template<typename T, typename U> void j1(decltype(T{.n = 0}));
148+
template<typename T, typename U> void j1(decltype(U{.n = 0})) = delete;
149+
template<typename T, typename U> void j2(decltype(T{.n = {0}})); // expected-note {{candidate}}
150+
template<typename T, typename U> void j2(decltype(U{.n = {0}})); // expected-note {{candidate}}
151+
template<typename T, typename U> void j3(decltype(T{.n = {{0}}})) = delete;
152+
template<typename T, typename U> void j3(decltype(U{.n = {{0}}}));
153+
void k() {
154+
j1<D, E>({}); // ok, selects D overload by SFINAE (too few braces for E)
155+
j2<D, E>({}); // expected-error {{ambiguous}}
156+
j3<D, E>({}); // ok, selects E overload by SFINAE (too many braces for D)
157+
}
131158
}

0 commit comments

Comments
 (0)