Index: clang/include/clang/Sema/Overload.h =================================================================== --- clang/include/clang/Sema/Overload.h +++ clang/include/clang/Sema/Overload.h @@ -533,11 +533,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 +568,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 +670,12 @@ 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 isInitializerListToType() const { return bool(InitializerListToType); } + void setInitializerListToType(QualType T) { + InitializerListToType = T.getAsOpaquePtr(); } - - void setStdInitializerListElement(bool V = true) { - StdInitializerListElement = V; + 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 (isInitializerListToType()) + OS << "Worst list element conversion: "; switch (ConversionKind) { case StandardConversion: OS << "Standard conversion: "; @@ -3801,16 +3801,46 @@ // 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.isInitializerListToType()) + StdInit1 = + S.isStdInitializerList(ICS1.getInitializerListToType(), nullptr); + if (ICS2.isInitializerListToType()) + 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.isInitializerListToType() && ICS2.isInitializerListToType()) { + if (auto *AT1 = + S.Context.getAsArrayType(ICS1.getInitializerListToType())) { + if (auto *AT2 = + S.Context.getAsArrayType(ICS2.getInitializerListToType())) { + if (AT1->getElementType() == AT2->getElementType()) { + if (auto CAT1 = dyn_cast(AT1)) { + if (auto CAT2 = dyn_cast(AT2)) { + if (CAT1->getSize() != CAT2->getSize()) + return CAT1->getSize().ult(CAT2->getSize()) + ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + } + llvm_unreachable("C++20 incomplete array init?"); + } else if (isa(AT2)) + llvm_unreachable("C++20 incomplete array init?"); + } + } + } + } } if (ICS1.isStandard()) @@ -5065,42 +5095,58 @@ // 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) { + InitTy = AT->getElementType(); + if (ConstantArrayType const *CT = dyn_cast(AT)) { + if (CT->getSize().ult(e)) + 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 = 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. + 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; } Index: clang/test/SemaCXX/overload-ary-bind.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/overload-ary-bind.cpp @@ -0,0 +1,41 @@ +// 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{{cannot convert}} +char (&b(int(&&)[2]))[2]; // #2 expected-note{{cannot convert}} + +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{{cannot convert}} + +void f() { + b({2}); // #1 expected-error{{no matching function}} +} +} // namespace Three