initializer list.
This previously led to some weird behaviors where we would unwrap the
initializer list expression and then try to use the DesignatedInitExprs
as constructor arguments.
Under the C++20 language rules, it's not valid to initialize a
reference-to-aggregate from a designated initializer list, but we have
historically accepted that, as do other compilers, and we continue to
accept that with this change. I've asked WG21 whether this should be
considered a wording defect.
/// has been set.
bool hasArrayFiller() const { return getArrayFiller(); }
+ // Determine whether this initializer list contains a designated initializer.
+ bool hasDesignatedInit() const {
+ return std::any_of(begin(), end(), [](const Stmt *S) {
+ return isa<DesignatedInitExpr>(S);
+ });
+ }
+
/// If this initializes a union, specifies which field in the
/// union to initialize.
///
"reference cannot be initialized with multiple values">;
def err_init_non_aggr_init_list : Error<
"initialization of non-aggregate type %0 with an initializer list">;
+def err_designated_init_for_non_aggregate : Error<
+ "initialization of non-aggregate type %0 with a designated initializer list">;
def err_init_reference_member_uninitialized : Error<
"reference member of type %0 uninitialized">;
def note_uninit_reference_member : Note<
/// Parenthesized list initialization failed at some point.
/// This is a C++20 feature.
FK_ParenthesizedListInitFailed,
+
+ // A designated initializer was provided for a non-aggregate type.
+ FK_DesignatedInitForNonAggregate,
};
private:
case FK_ExplicitConstructor:
case FK_AddressOfUnaddressableFunction:
case FK_ParenthesizedListInitFailed:
+ case FK_DesignatedInitForNonAggregate:
return false;
case FK_ReferenceInitOverloadFailed:
return;
}
+ // C++20 [dcl.init.list]p3:
+ // - If the braced-init-list contains a designated-initializer-list, T shall
+ // be an aggregate class. [...] Aggregate initialization is performed.
+ //
+ // We allow arrays here too in order to support array designators.
+ //
+ // FIXME: This check should precede the handling of reference initialization.
+ // We follow other compilers in allowing things like 'Aggr &&a = {.x = 1};'
+ // as a tentative DR resolution.
+ bool IsDesignatedInit = InitList->hasDesignatedInit();
+ if (!DestType->isAggregateType() && IsDesignatedInit) {
+ Sequence.SetFailed(
+ InitializationSequence::FK_DesignatedInitForNonAggregate);
+ return;
+ }
+
// C++11 [dcl.init.list]p3, per DR1467:
// - If T is a class type and the initializer list has a single element of
// type cv U, where U is T or a class derived from T, the object is
// (8.5.2 [dcl.init.string]), initialization is performed as described
// in that section.
// - Otherwise, if T is an aggregate, [...] (continue below).
- if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1) {
+ if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1 &&
+ !IsDesignatedInit) {
if (DestType->isRecordType()) {
QualType InitType = InitList->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
// - If T is an aggregate, aggregate initialization is performed.
if ((DestType->isRecordType() && !DestType->isAggregateType()) ||
(S.getLangOpts().CPlusPlus11 &&
- S.isStdInitializerList(DestType, nullptr))) {
+ S.isStdInitializerList(DestType, nullptr) && !IsDesignatedInit)) {
if (S.getLangOpts().CPlusPlus11) {
// - Otherwise, if the initializer list has no elements and T is a
// class type with a default constructor, the object is
TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this,
/*VerifyOnly=*/false);
break;
+
+ case FK_DesignatedInitForNonAggregate:
+ InitListExpr *InitList = cast<InitListExpr>(Args[0]);
+ S.Diag(Kind.getLocation(), diag::err_designated_init_for_non_aggregate)
+ << Entity.getType() << InitList->getSourceRange();
+ break;
}
PrintInitLocationNote(S, Entity);
case FK_ParenthesizedListInitFailed:
OS << "parenthesized list initialization failed";
break;
+
+ case FK_DesignatedInitForNonAggregate:
+ OS << "designated initializer for non-aggregate type";
+ break;
}
OS << '\n';
return;
if (!S.isCompleteType(From->getBeginLoc(), InitTy))
return Result;
+ // C++20 [over.ics.list]/2:
+ // If the initializer list is a designated-initializer-list, a conversion
+ // is only possible if the parameter has an aggregate type
+ //
+ // FIXME: The exception for reference initialization here is not part of the
+ // language rules, but follow other compilers in adding it as a tentative DR
+ // resolution.
+ bool IsDesignatedInit = From->hasDesignatedInit();
+ if (!ToType->isAggregateType() && !ToType->isReferenceType() &&
+ IsDesignatedInit)
+ return Result;
+
// Per DR1467:
// If the parameter type is a class X and the initializer list has a single
// element of type cv U, where U is X or a class derived from X, the
// and the initializer list has a single element that is an
// appropriately-typed string literal (8.5.2 [dcl.init.string]), the
// implicit conversion sequence is the identity conversion.
- if (From->getNumInits() == 1) {
+ if (From->getNumInits() == 1 && !IsDesignatedInit) {
if (ToType->isRecordType()) {
QualType InitType = From->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, ToType) ||
// default-constructible, and if all the elements of the initializer list
// can be implicitly converted to X, the implicit conversion sequence is
// the worst conversion necessary to convert an element of the list to X.
- if (AT || S.isStdInitializerList(ToType, &InitTy)) {
+ if ((AT || S.isStdInitializerList(ToType, &InitTy)) && !IsDesignatedInit) {
unsigned e = From->getNumInits();
ImplicitConversionSequence DfltElt;
DfltElt.setBad(BadConversionSequence::no_conversion, QualType(),
// If the initializer list has a single element that is reference-related
// to the parameter type, we initialize the reference from that.
- if (From->getNumInits() == 1) {
+ if (From->getNumInits() == 1 && !IsDesignatedInit) {
Expr *Init = From->getInit(0);
QualType T2 = Init->getType();
int windows_size[((sizeof(windows) / sizeof(struct rect)) == 6)? 1 : -1];
struct rect windows_bad[3] = {
- [2].top_left = { { .x = 1.1 } }, // expected-error{{designator in initializer for scalar type}}
+ [2].top_left = { { .x = 1.1 } }, // expected-error{{initialization of non-aggregate type 'double' with a designated initializer list}}
[1].top_left = { .x = 1.1 }
};
svint8_t bad_brace_init_int8_1 = {local_int8, 0}; // expected-warning {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2 = {0}; // expected-error {{incompatible type 'int'}}
svint8_t bad_brace_init_int8_3 = {local_int16}; // expected-error {{incompatible type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}}
+ svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{initialization of non-aggregate type 'svint8_t' (aka '__SVInt8_t') with a designated initializer list}}
svint8_t bad_brace_init_int8_5 = {{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6 = {{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
j3<D, E>({}); // ok, selects E overload by SFINAE (too many braces for D)
}
}
+
+namespace no_unwrap {
+ template<typename T> struct X {
+ static_assert(false, "should not be instantiated");
+ };
+ struct Q {
+ template<typename T, typename U = typename X<T>::type> Q(T&&);
+ };
+
+ // Ensure that we do not try to call 'Q::Q(.a = 1)' here.
+ void g() { Q q = {.a = 1}; } // expected-error {{initialization of non-aggregate type 'Q' with a designated initializer list}}
+
+ struct S { int a; };
+ void h(Q q);
+ void h(S s);
+
+ // OK, does not instantiate X<void&> (!).
+ void i() {
+ h({.a = 1});
+ }
+}
svint8_t bad_brace_init_int8_1 = {local_int8, 0}; // expected-error {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2 = {0}; // expected-error {{rvalue of type 'int'}}
svint8_t bad_brace_init_int8_3 = {local_int16}; // expected-error {{lvalue of type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}} expected-warning {{array designators are a C99 extension}}
+ svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error-re {{initialization of non-aggregate type 'svint8_t'{{.*}} with a designated initializer list}} expected-warning {{array designators are a C99 extension}}
svint8_t bad_brace_init_int8_5 = {{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6 = {{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_1{local_int8, 0}; // expected-error {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2{0}; // expected-error {{rvalue of type 'int'}}
svint8_t bad_brace_init_int8_3{local_int16}; // expected-error {{lvalue of type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4{[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}} expected-warning {{array designators are a C99 extension}}
+ svint8_t bad_brace_init_int8_4{[0] = local_int8}; // expected-error-re {{initialization of non-aggregate type 'svint8_t'{{.*}} with a designated initializer list}} expected-warning {{array designators are a C99 extension}}
svint8_t bad_brace_init_int8_5{{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6{{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
svint8_t wrapper_init_int8{wrapper<svint8_t>()};