Index: clang/include/clang/Sema/Overload.h =================================================================== --- clang/include/clang/Sema/Overload.h +++ clang/include/clang/Sema/Overload.h @@ -539,6 +539,18 @@ /// sequence only represents the worst element conversion. unsigned StdInitializerListElement : 1; + /// Whether the target is used with an initializer list. + /// If so the rank algorithm needs to know about the number of elements + /// in the std::initializer_list or array. This information isn't stored in + /// the conversion sequences. + unsigned ExtendedTypeInfo : 1; + + /// The std::initializer_list expression to convert from. + const InitListExpr *StandardInitializerListFrom{nullptr}; + + /// The std::initializer_list or array to convert to. + void *StandardInitializerListToType{nullptr}; + void setKind(Kind K) { destruct(); ConversionKind = K; @@ -568,13 +580,17 @@ }; ImplicitConversionSequence() - : ConversionKind(Uninitialized), StdInitializerListElement(false) { + : ConversionKind(Uninitialized), StdInitializerListElement(false), + ExtendedTypeInfo(false) { Standard.setAsIdentityConversion(); } ImplicitConversionSequence(const ImplicitConversionSequence &Other) : ConversionKind(Other.ConversionKind), - StdInitializerListElement(Other.StdInitializerListElement) { + StdInitializerListElement(Other.StdInitializerListElement), + ExtendedTypeInfo(Other.ExtendedTypeInfo), + StandardInitializerListFrom(Other.StandardInitializerListFrom), + StandardInitializerListToType(Other.StandardInitializerListToType) { switch (ConversionKind) { case Uninitialized: break; case StandardConversion: Standard = Other.Standard; break; @@ -676,8 +692,25 @@ return StdInitializerListElement; } - void setStdInitializerListElement(bool V = true) { + void setStdInitializerListElement(bool V = true, + const InitListExpr *From = nullptr, + QualType ToType = QualType()) { StdInitializerListElement = V; + ExtendedTypeInfo = true; + StandardInitializerListFrom = From; + StandardInitializerListToType = ToType.getAsOpaquePtr(); + } + + bool hasExtendedTypeInfo() const { return ExtendedTypeInfo; } + + const InitListExpr *getStdInitializerListFrom() const { + assert(ExtendedTypeInfo); + return StandardInitializerListFrom; + } + + QualType getStdInitializerListToType() const { + assert(ExtendedTypeInfo); + return QualType::getFromOpaquePtr(StandardInitializerListToType); } /// Form an "implicit" conversion sequence from nullptr_t to bool, for a Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -3686,6 +3686,100 @@ ICS.UserDefined.Before.DeprecatedStringLiteralToCharPtr); } +namespace { +class CompareListInitializationSequences { + // List-initialization sequence L1 is a better conversion sequence than + // list-initialization sequence L2 if: + // ... + // - C++17: + // L1 converts to type "array of N1 T", L2 converts to type "array of N2 T", + // and N1 is smaller than N2., + // - C++20 + // L1 and L2 convert to arrays of the same element type, and either the + // number of elements n1 initialized by L1 is less than the number of + // elements n2 initialized by L2, or n1=n2 and L2 converts to an array of + // unknown bound and L1 does not, + + struct Conversion { + enum ConversionKind { + Bad, + // Conversion to an array of known bound. The source doesn't have too + // many elements. + ConstantArray, + // Conversion to an array of unknown bound, requires C++20. + IncompleteArray + }; + ConversionKind Kind{Bad}; + // The number of elements the source has less than the target. + // - Zero means the source and target have the same number of elements, + // or the target is an array of unknown bound. + uint64_t Diff{0}; + QualType ElementType; + + Conversion(Sema &S, const ImplicitConversionSequence &ICS) { + const InitListExpr *From = ICS.getStdInitializerListFrom(); + QualType ToType = ICS.getStdInitializerListToType(); + + if (ToType->isIncompleteArrayType() && S.getLangOpts().CPlusPlus20) { + Kind = IncompleteArray; + ElementType = + S.Context.getAsIncompleteArrayType(ToType)->getElementType(); + + } else if (ToType->isConstantArrayType()) { + const auto *Array = S.Context.getAsConstantArrayType(ToType); + Diff = Array->getSize().getZExtValue(); + if (From->getNumInits() <= Diff) { + Diff -= From->getNumInits(); + Kind = ConstantArray; + } + ElementType = Array->getElementType(); + } + } + }; + + static ImplicitConversionSequence::CompareKind Compare(const Conversion &C1, + const Conversion &C2) { + if (C1.Kind != Conversion::Bad && C1.Kind == Conversion::Bad) + return ImplicitConversionSequence::Better; + + if (C1.Kind == Conversion::Bad && C1.Kind != Conversion::Bad) + return ImplicitConversionSequence::Worse; + + if (C1.Kind != Conversion::Bad && C1.Kind != Conversion::Bad) { + if (C1.Diff == 0 && C2.Diff == 0) { + if (C1.Kind == Conversion::ConstantArray && + C2.Kind == Conversion::IncompleteArray) + return ImplicitConversionSequence::Better; + if (C1.Kind == Conversion::IncompleteArray && + C2.Kind == Conversion::ConstantArray) + return ImplicitConversionSequence::Worse; + } else { + if (C1.Diff < C2.Diff) + return ImplicitConversionSequence::Better; + if (C1.Diff > C2.Diff) + return ImplicitConversionSequence::Worse; + } + } + + return ImplicitConversionSequence::Indistinguishable; + } + +public: + ImplicitConversionSequence::CompareKind Result{ + ImplicitConversionSequence::Indistinguishable}; + + CompareListInitializationSequences(Sema &S, + const ImplicitConversionSequence &ICS1, + const ImplicitConversionSequence &ICS2) { + Conversion C1(S, ICS1); + Conversion C2(S, ICS2); + if (!C1.ElementType.isNull() && !C2.ElementType.isNull() && + S.Context.hasSameType(C1.ElementType, C2.ElementType)) + Result = Compare(C1, C2); + } +}; +} // namespace + /// CompareImplicitConversionSequences - Compare two implicit /// conversion sequences to determine whether one is better than the /// other or if they are indistinguishable (C++ 13.3.3.2). @@ -3765,6 +3859,12 @@ if (!ICS1.isStdInitializerListElement() && ICS2.isStdInitializerListElement()) return ImplicitConversionSequence::Worse; + + if (ICS1.hasExtendedTypeInfo() && ICS2.hasExtendedTypeInfo()) { + CompareListInitializationSequences LIS(S, ICS1, ICS2); + if (LIS.Result != ImplicitConversionSequence::Indistinguishable) + return LIS.Result; + } } if (ICS1.isStandard()) @@ -5047,9 +5147,17 @@ Result.Standard.setAsIdentityConversion(); Result.Standard.setFromType(ToType); Result.Standard.setAllToTypes(ToType); + } else if (ToType->isConstantArrayType()) { + // Has the initializer list exactly N elements or fewer than N elements? + uint64_t Size = S.getASTContext() + .getAsConstantArrayType(ToType) + ->getSize() + .getZExtValue(); + if (From->getNumInits() > Size) + Result.setBad(BadConversionSequence::no_conversion, From, ToType); } - Result.setStdInitializerListElement(toStdInitializerList); + Result.setStdInitializerListElement(toStdInitializerList, From, ToType); return Result; } Index: clang/test/SemaCXX/overload-call.cpp =================================================================== --- clang/test/SemaCXX/overload-call.cpp +++ clang/test/SemaCXX/overload-call.cpp @@ -688,3 +688,37 @@ f(pmf); } } + +#if __cplusplus >= 201103L +namespace InitializerListToArray { +void f(int(&&)[2]); // expected-note {{candidate function not viable}} +void f(int(&&)[3]); // expected-note {{candidate function not viable}} +void f(int(&&)[4]); // expected-note {{candidate function not viable}} + +void g() { + f({1}); + f({1, 2}); + f({1, 2, 3}); + f({1, 2, 3, 4}); + f({1, 2, 3, 4, 5}); // expected-error {{no matching function for call to 'f'}} +} + +struct S { + S(); + S(double); +}; + +void h(S(&&)[2]); // expected-note {{candidate function not viable}} +void h(S(&&)[3]); // expected-note {{candidate function not viable}} +void h(S(&&)[4]); // expected-note {{candidate function not viable}} + +void i() { + h({1}); + h({1, 2}); + h({1, 2, 3}); + h({1, 2, 3, 4}); + h({1, 2, 3, 4, 5}); // expected-error {{no matching function for call to 'h'}} +} + +} // namespace InitializerListToArray +#endif