diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 65476322c77df..e8f5b70f8f73b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4903,6 +4903,13 @@ class InitListExpr : public Expr { /// 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(S); + }); + } + /// If this initializes a union, specifies which field in the /// union to initialize. /// diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c852aeeb401ff..379554baaaa1f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2190,6 +2190,8 @@ def err_reference_has_multiple_inits : Error< "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< diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index e5a98ba97f4f1..fcfb56f9731ef 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -1107,6 +1107,9 @@ class InitializationSequence { /// 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: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4b62d9cea9a99..5fe417fa6351d 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3595,6 +3595,7 @@ bool InitializationSequence::isAmbiguous() const { case FK_ExplicitConstructor: case FK_AddressOfUnaddressableFunction: case FK_ParenthesizedListInitFailed: + case FK_DesignatedInitForNonAggregate: return false; case FK_ReferenceInitOverloadFailed: @@ -4432,6 +4433,22 @@ static void TryListInitialization(Sema &S, 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 @@ -4443,7 +4460,8 @@ static void TryListInitialization(Sema &S, // (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) || @@ -4485,7 +4503,7 @@ static void TryListInitialization(Sema &S, // - 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 @@ -9794,6 +9812,12 @@ bool InitializationSequence::Diagnose(Sema &S, TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, /*VerifyOnly=*/false); break; + + case FK_DesignatedInitForNonAggregate: + InitListExpr *InitList = cast(Args[0]); + S.Diag(Kind.getLocation(), diag::err_designated_init_for_non_aggregate) + << Entity.getType() << InitList->getSourceRange(); + break; } PrintInitLocationNote(S, Entity); @@ -9964,6 +9988,10 @@ void InitializationSequence::dump(raw_ostream &OS) const { case FK_ParenthesizedListInitFailed: OS << "parenthesized list initialization failed"; break; + + case FK_DesignatedInitForNonAggregate: + OS << "designated initializer for non-aggregate type"; + break; } OS << '\n'; return; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5cbd9e8a46234..aa40bff4c575d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5130,6 +5130,18 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, 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 @@ -5140,7 +5152,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, // 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) || @@ -5178,7 +5190,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType 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(), @@ -5320,7 +5332,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, // 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(); diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c index e039f18364639..31a3380b5db7d 100644 --- a/clang/test/Sema/designated-initializers.c +++ b/clang/test/Sema/designated-initializers.c @@ -70,7 +70,7 @@ struct rect windows[] = { 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 } }; diff --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c index 8ef30a12f1128..bbc36e32181f1 100644 --- a/clang/test/Sema/sizeless-1.c +++ b/clang/test/Sema/sizeless-1.c @@ -89,7 +89,7 @@ void func(int sel) { 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}} diff --git a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp index c6339116af8bb..3cc3f9cf6dff4 100644 --- a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp +++ b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp @@ -156,3 +156,24 @@ namespace deduction { j3({}); // ok, selects E overload by SFINAE (too many braces for D) } } + +namespace no_unwrap { + template struct X { + static_assert(false, "should not be instantiated"); + }; + struct Q { + template::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 i() { + h({.a = 1}); + } +} diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp index 6c8c873f494f7..ed0416f17995b 100644 --- a/clang/test/SemaCXX/sizeless-1.cpp +++ b/clang/test/SemaCXX/sizeless-1.cpp @@ -103,7 +103,7 @@ void func(int sel) { 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}} @@ -422,7 +422,7 @@ void cxx_only(int sel) { 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()};