Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4461,7 +4461,8 @@ "; remove &}7">; def note_ovl_candidate_bad_list_argument : Note< "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " - "cannot convert initializer list argument to %4">; + "%select{cannot convert initializer list|too few initializers in list" + "|too many initializers in list}7 argument to %4">; def note_ovl_candidate_bad_overload : Note< "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " "no overload of %4 matching %3 for %ordinal5 argument">; Index: clang/include/clang/Sema/Overload.h =================================================================== --- clang/include/clang/Sema/Overload.h +++ clang/include/clang/Sema/Overload.h @@ -469,7 +469,9 @@ unrelated_class, bad_qualifiers, lvalue_ref_to_rvalue, - rvalue_ref_to_lvalue + rvalue_ref_to_lvalue, + too_few_initializers, + too_many_initializers, }; // This can be null, e.g. for implicit object arguments. @@ -533,11 +535,11 @@ }; /// ConversionKind - The kind of implicit conversion sequence. - unsigned ConversionKind : 30; + unsigned ConversionKind; - /// Whether the target is really a std::initializer_list, and the - /// sequence only represents the worst element conversion. - unsigned StdInitializerListElement : 1; + /// If from an initializer list, this is the (opaque) type that is + /// initialized. + void *InitializerListToType; void setKind(Kind K) { destruct(); @@ -568,13 +570,13 @@ }; ImplicitConversionSequence() - : ConversionKind(Uninitialized), StdInitializerListElement(false) { + : ConversionKind(Uninitialized), InitializerListToType(nullptr) { Standard.setAsIdentityConversion(); } ImplicitConversionSequence(const ImplicitConversionSequence &Other) : ConversionKind(Other.ConversionKind), - StdInitializerListElement(Other.StdInitializerListElement) { + InitializerListToType(Other.InitializerListToType) { switch (ConversionKind) { case Uninitialized: break; case StandardConversion: Standard = Other.Standard; break; @@ -670,14 +672,14 @@ Standard.setAllToTypes(T); } - /// Whether the target is really a std::initializer_list, and the - /// sequence only represents the worst element conversion. - bool isStdInitializerListElement() const { - return StdInitializerListElement; + bool hasInitializerListToType() const { + return bool(InitializerListToType); } - - void setStdInitializerListElement(bool V = true) { - StdInitializerListElement = V; + void setInitializerListToType(QualType T) { + InitializerListToType = T.getAsOpaquePtr(); + } + QualType getInitializerListToType() const { + return QualType::getFromOpaquePtr(InitializerListToType); } /// 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 @@ -541,8 +541,8 @@ /// error. Useful for debugging overloading issues. void ImplicitConversionSequence::dump() const { raw_ostream &OS = llvm::errs(); - if (isStdInitializerListElement()) - OS << "Worst std::initializer_list element conversion: "; + if (hasInitializerListToType()) + OS << "Worst list element conversion: "; switch (ConversionKind) { case StandardConversion: OS << "Standard conversion: "; @@ -3801,16 +3801,39 @@ // list-initialization sequence L2 if: // - L1 converts to std::initializer_list for some X and L2 does not, or, // if not that, - // - L1 converts to type "array of N1 T", L2 converts to type "array of N2 T", - // and N1 is smaller than N2., + // — L1 and L2 convert to arrays of the same element type, and either the + // number of elements n_1 initialized by L1 is less than the number of + // elements n_2 initialized by L2, or (unimplemented:C++20) n_1 = n_2 and L2 + // converts to an array of unknown bound and L1 does not, // even if one of the other rules in this paragraph would otherwise apply. if (!ICS1.isBad()) { - if (ICS1.isStdInitializerListElement() && - !ICS2.isStdInitializerListElement()) + bool StdInit1 = false, StdInit2 = false; + + if (ICS1.hasInitializerListToType()) + StdInit1 = + S.isStdInitializerList(ICS1.getInitializerListToType(), nullptr); + if (ICS2.hasInitializerListToType()) + StdInit2 = + S.isStdInitializerList(ICS2.getInitializerListToType(), nullptr); + + if (StdInit1 && !StdInit2) return ImplicitConversionSequence::Better; - if (!ICS1.isStdInitializerListElement() && - ICS2.isStdInitializerListElement()) + if (!StdInit1 && StdInit2) return ImplicitConversionSequence::Worse; + + if (ICS1.hasInitializerListToType() && ICS2.hasInitializerListToType()) { + if (auto *AT1 = S.Context.getAsArrayType(ICS1.getInitializerListToType())) + if (auto *AT2 = + S.Context.getAsArrayType(ICS2.getInitializerListToType())) + if (S.Context.hasSameUnqualifiedType(AT1->getElementType(), + AT2->getElementType())) + if (const auto *CAT1 = dyn_cast(AT1)) + if (const auto *CAT2 = dyn_cast(AT2)) + if (CAT1->getSize() != CAT2->getSize()) + return CAT1->getSize().ult(CAT2->getSize()) + ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + } } if (ICS1.isStandard()) @@ -5065,42 +5088,64 @@ // can be implicitly converted to X, the implicit conversion sequence is // the worst conversion necessary to convert an element of the list to X. // - // FIXME: We're missing a lot of these checks. - bool toStdInitializerList = false; - QualType X; - if (ToType->isArrayType()) - X = S.Context.getAsArrayType(ToType)->getElementType(); - else - toStdInitializerList = S.isStdInitializerList(ToType, &X); - if (!X.isNull()) { - for (unsigned i = 0, e = From->getNumInits(); i < e; ++i) { - Expr *Init = From->getInit(i); - ImplicitConversionSequence ICS = - TryCopyInitialization(S, Init, X, SuppressUserConversions, - InOverloadResolution, - AllowObjCWritebackConversion); - // If a single element isn't convertible, fail. - if (ICS.isBad()) { - Result = ICS; - break; + QualType InitTy = ToType; + ArrayType const *AT = S.Context.getAsArrayType(ToType); + if (AT || S.isStdInitializerList(ToType, &InitTy)) { + unsigned e = From->getNumInits(); + bool Clear = true; + if (AT) { + // Result has been initialized above as a BadConversionSequence + InitTy = AT->getElementType(); + if (ConstantArrayType const *CT = dyn_cast(AT)) { + if (CT->getSize().ult(e)) { + Result.Bad.Kind = BadConversionSequence::too_many_initializers; + Clear = false; // Too many inits, fatally bad + } else if (CT->getSize().ugt(e)) { + // Need an init from empty {}, is there one? + // FIXME: Is there a simpler way than this? + InitListExpr EmptyList(S.Context, From->getEndLoc(), None, + From->getEndLoc()); + EmptyList.setType(S.Context.VoidTy); + auto ICS = TryListConversion( + S, &EmptyList, InitTy, SuppressUserConversions, + InOverloadResolution, AllowObjCWritebackConversion); + if (ICS.isBad()) + Result.Bad.Kind = BadConversionSequence::too_few_initializers; + else + Result = ICS; + Clear = false; + } } - // Otherwise, look for the worst conversion. - if (Result.isBad() || CompareImplicitConversionSequences( - S, From->getBeginLoc(), ICS, Result) == - ImplicitConversionSequence::Worse) - Result = ICS; } - - // For an empty list, we won't have computed any conversion sequence. - // Introduce the identity conversion sequence. - if (From->getNumInits() == 0) { + if (Clear) { Result.setStandard(); Result.Standard.setAsIdentityConversion(); - Result.Standard.setFromType(ToType); - Result.Standard.setAllToTypes(ToType); - } + Result.Standard.setFromType(InitTy); + Result.Standard.setAllToTypes(InitTy); + } + + if (!Result.isBad()) + for (unsigned i = 0; i < e; ++i) { + Expr *Init = From->getInit(i); + ImplicitConversionSequence ICS = TryCopyInitialization( + S, Init, InitTy, SuppressUserConversions, InOverloadResolution, + AllowObjCWritebackConversion); + // Keep the worse conversion. + // FIXME: Sequences are not totally ordered, so 'worse' can be + // ambiguous. CWG has been informed. + if (CompareImplicitConversionSequences(S, From->getBeginLoc(), ICS, + Result) == + ImplicitConversionSequence::Worse) { + Result = ICS; + // Bail as soon as we find something unconvertible. + if (Result.isBad()) + break; + } + } + + // Record the type being initialized so that we may compare sequences + Result.setInitializerListToType(ToType); - Result.setStdInitializerListElement(toStdInitializerList); return Result; } @@ -5479,6 +5524,10 @@ case BadConversionSequence::no_conversion: case BadConversionSequence::unrelated_class: break; + + case BadConversionSequence::too_few_initializers: + case BadConversionSequence::too_many_initializers: + llvm_unreachable("Lists are not objects"); } return Diag(From->getBeginLoc(), diag::err_member_function_call_bad_type) @@ -10524,7 +10573,11 @@ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_list_argument) << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy - << ToTy << (unsigned)isObjectArgument << I + 1; + << ToTy << (unsigned)isObjectArgument << I + 1 + << (Conv.Bad.Kind == BadConversionSequence::too_few_initializers ? 1 + : Conv.Bad.Kind == BadConversionSequence::too_many_initializers + ? 2 + : 0); MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl); return; } Index: clang/test/SemaCXX/overload-ary-bind.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/overload-ary-bind.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify %s + +namespace One { +char (&b(int(&&)[1]))[1]; // #1 expected-note{{too many initializers}} +char (&b(int(&&)[2]))[2]; // #2 expected-note{{too many initializers}} + +void f() { + static_assert(sizeof(b({1})) == 1); // #1 + static_assert(sizeof(b({1, 2})) == 2); // #2 + + b({1, 2, 3}); // expected-error{{no matching function}} +} +} // namespace One + +namespace Two { +struct Bob { + Bob(int = 1); +}; + +char (&b(Bob(&&)[1]))[1]; // #1 +char (&b(Bob(&&)[2]))[2]; // #2 + +void f() { + static_assert(sizeof(b({})) == 1); // #1 + static_assert(sizeof(b({Bob()})) == 1); // #1 + static_assert(sizeof(b({2, Bob()})) == 2); // #2 +} +} // namespace Two + +namespace Three { +struct Kevin { + Kevin(int); +}; + +char (&b(Kevin(&&)[2]))[2]; // #2 expected-note{{too few initializers}} + +void f() { + b({2}); // #1 expected-error{{no matching function}} +} +} // namespace Three + +namespace Four { +char (&b(int(&&)[1], float))[1]; // #1 expected-note{{candidate}} +char (&b(int(&&)[1], double))[2]; // #2 expected-note{{candidate}} + +char (&c(float, int(&&)[1]))[1]; // #1 expected-note{{candidate}} +char (&c(double, int(&&)[1]))[2]; // #2 expected-note{{candidate}} + +void f() { + b({1}, 0); // expected-error{{is ambiguous}} + c(0, {1}); // expected-error{{is ambiguous}} +} +} // namespace Four + +typedef decltype(sizeof(char)) size_t; +namespace std { +// sufficient initializer list +template +class initializer_list { + const _E *__begin_; + size_t __size_; + + constexpr initializer_list(const _E *__b, size_t __s) + : __begin_(__b), + __size_(__s) {} + +public: + typedef _E value_type; + typedef const _E &reference; + typedef const _E &const_reference; + typedef size_t size_type; + + typedef const _E *iterator; + typedef const _E *const_iterator; + + constexpr initializer_list() : __begin_(nullptr), __size_(0) {} + + constexpr size_t size() const { return __size_; } + constexpr const _E *begin() const { return __begin_; } + constexpr const _E *end() const { return __begin_ + __size_; } +}; +} // namespace std + +namespace Five { +struct any { + template any(T); +}; +char (&f(std::initializer_list))[1]; // #1 +char (&f(std::initializer_list))[2]; // #2 + +void g() { + // Should pick #2 as string-literal->char * is disallowed + static_assert(sizeof(f({"hello", 3})) == 2); +} + +} // namespace Five