Index: clang/include/clang/Sema/Overload.h =================================================================== --- clang/include/clang/Sema/Overload.h +++ clang/include/clang/Sema/Overload.h @@ -533,7 +533,10 @@ }; /// ConversionKind - The kind of implicit conversion sequence. - unsigned ConversionKind; + unsigned ConversionKind : 31; + + // Whether the initializer list was of an incomplete array. + unsigned InitializerListOfIncompleteArray : 1; /// If from an initializer list, this is the (opaque) type that is /// initialized. @@ -568,12 +571,16 @@ }; ImplicitConversionSequence() - : ConversionKind(Uninitialized), InitializerListToType(nullptr) { + : ConversionKind(Uninitialized), + InitializerListOfIncompleteArray(false), + InitializerListToType(nullptr) { Standard.setAsIdentityConversion(); } ImplicitConversionSequence(const ImplicitConversionSequence &Other) : ConversionKind(Other.ConversionKind), + InitializerListOfIncompleteArray( + Other.InitializerListOfIncompleteArray), InitializerListToType(Other.InitializerListToType) { switch (ConversionKind) { case Uninitialized: break; @@ -671,9 +678,13 @@ } bool isInitializerListToType() const { return bool(InitializerListToType); } - void setInitializerListToType(QualType T) { + void setInitializerListToType(QualType T, bool IA) { + InitializerListOfIncompleteArray = IA; InitializerListToType = T.getAsOpaquePtr(); } + bool isInitializerListOfIncompleteArray() const { + return bool(InitializerListOfIncompleteArray); + } QualType getInitializerListToType() const { return QualType::getFromOpaquePtr(InitializerListToType); } Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -5804,14 +5804,14 @@ // FIXME: Consider also unwrapping array of unknown bound and VLA. if (auto *CAT1 = dyn_cast(AT1)) { auto *CAT2 = dyn_cast(AT2); - if (!(CAT2 && CAT1->getSize() == CAT2->getSize()) && - !(getLangOpts().CPlusPlus20 && AllowPiMismatch && - isa(AT2))) + if (!((CAT2 && CAT1->getSize() == CAT2->getSize()) || + (AllowPiMismatch && getLangOpts().CPlusPlus20 && + isa(AT2)))) return; } else if (isa(AT1)) { - if (!isa(AT2) && - !(getLangOpts().CPlusPlus20 && AllowPiMismatch && - isa(AT2))) + if (!(isa(AT2) || + (AllowPiMismatch && getLangOpts().CPlusPlus20 && + isa(AT2)))) return; } else { return; Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -3816,8 +3816,8 @@ // if not that, // — 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, + // elements n_2 initialized by L2, or (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()) { bool StdInit1 = false, StdInit2 = false; @@ -3840,16 +3840,20 @@ 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?"); + // Both to arrays of the same element type + const auto *CAT1 = cast(AT1); + const auto *CAT2 = cast(AT2); + if (CAT1->getSize() != CAT2->getSize()) + // Different sized, the smaller wins + return CAT1->getSize().ult(CAT2->getSize()) + ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + if (ICS1.isInitializerListOfIncompleteArray() != + ICS2.isInitializerListOfIncompleteArray()) + // One is incomplete, it loses + return ICS2.isInitializerListOfIncompleteArray() + ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; } } } @@ -5011,9 +5015,15 @@ ImplicitConversionSequence Result; Result.setBad(BadConversionSequence::no_conversion, From, ToType); - // We need a complete type for what follows. Incomplete types can never be - // initialized from init lists. - if (!S.isCompleteType(From->getBeginLoc(), ToType)) + // We need a complete type for what follows. With one C++20 exception, + // incomplete types can never be initialized from init lists. + QualType InitTy = ToType; + const ArrayType *AT = S.Context.getAsArrayType(ToType); + if (AT && S.getLangOpts().CPlusPlus20) + if (const auto *IAT = dyn_cast(AT)) + // C++20 allows list initialization of an incomplete array type. + InitTy = IAT->getElementType(); + if (!S.isCompleteType(From->getBeginLoc(), InitTy)) return Result; // Per DR1467: @@ -5037,18 +5047,16 @@ AllowObjCWritebackConversion); } - if (const auto *AT = S.Context.getAsArrayType(ToType)) { - if (S.IsStringInit(From->getInit(0), AT)) { - InitializedEntity Entity = + if (AT && S.IsStringInit(From->getInit(0), AT)) { + InitializedEntity Entity = InitializedEntity::InitializeParameter(S.Context, ToType, /*Consumed=*/false); - if (S.CanPerformCopyInitialization(Entity, From)) { - Result.setStandard(); - Result.Standard.setAsIdentityConversion(); - Result.Standard.setFromType(ToType); - Result.Standard.setAllToTypes(ToType); - return Result; - } + if (S.CanPerformCopyInitialization(Entity, From)) { + Result.setStandard(); + Result.Standard.setAsIdentityConversion(); + Result.Standard.setFromType(ToType); + Result.Standard.setAllToTypes(ToType); + return Result; } } } @@ -5067,11 +5075,11 @@ // can be implicitly converted to X, the implicit conversion sequence is // the worst conversion necessary to convert an element of the list to X. // - QualType InitTy = ToType; - ArrayType const *AT = S.Context.getAsArrayType(ToType); if (AT || S.isStdInitializerList(ToType, &InitTy)) { unsigned e = From->getNumInits(); bool Clear = true; + auto CTT = ToType; + bool OIA = false; if (AT) { InitTy = AT->getElementType(); if (ConstantArrayType const *CT = dyn_cast(AT)) { @@ -5090,6 +5098,16 @@ Result = ICS; Clear = false; } + } else { + assert(isa(AT) && "Expected incomplete array"); + if (e) { + OIA = true; + llvm::APInt Size(S.Context.getTypeSize(S.Context.getSizeType()), e); + CTT = S.Context.getConstantArrayType(InitTy, Size, nullptr, + ArrayType::Normal, 0); + } else + // Cannot convert to zero-sized. + Clear = false; } } if (Clear) { @@ -5116,8 +5134,9 @@ } } - // Record the type being initialized so that we may compare sequences - Result.setInitializerListToType(ToType); + if (!Result.isBad()) + // Record the type being initialized so that we may compare sequences + Result.setInitializerListToType(CTT, OIA); return Result; } Index: clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp =================================================================== --- clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp +++ clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -77,3 +77,66 @@ } } // namespace Four + +namespace Five { +// from the paper +char (&b(int(&&)[]))[1]; // #1 +char (&b(long(&&)[]))[2]; // #2 +char (&b(int(&&)[1]))[3]; // #3 +char (&b(long(&&)[1]))[4]; // #4 +char (&b(int(&&)[2]))[5]; // #5 +#if __cplusplus < 202002 + // expected-note@-6{{cannot convert initializer}} + // expected-note@-6{{cannot convert initializer}} + // expected-note@-6{{cannot convert initializer}} + // expected-note@-6{{cannot convert initializer}} + // expected-note@-6{{cannot convert initializer}} +#endif + +void f() { + static_assert(sizeof(b({1})) == 3); + static_assert(sizeof(b({1, 2})) == 5); + static_assert(sizeof(b({1, 2, 3})) == 1); +#if __cplusplus < 202002 + // expected-error@-2{{no matching function}} +#endif +} +} // namespace Five + +#if __cplusplus >= 202002 +namespace Six { +// from over.ics.rank 3.1 +char (&f(int(&&)[]))[1]; // #1 +char (&f(double(&&)[]))[2]; // #2 +char (&f(int(&&)[2]))[3]; // #3 + +void toto() { + // Calls #1: Better than #2 due to conversion, better than #3 due to bounds + static_assert(sizeof(f({1})) == 1); + + // Calls #2: Identity conversion is better than floating-integral conversion + static_assert(sizeof(f({1.0})) == 2); + + // Calls #2: Identity conversion is better than floating-integral conversion + static_assert(sizeof(f({1.0, 2.0})) == 2); + + // Calls #3: Converting to array of known bound is better than to unknown + // bound, and an identity conversion is better than + // floating-integral conversion + static_assert(sizeof(f({1, 2})) == 3); +} + +} // namespace Six + +namespace Seven { + +char (&f(int(&&)[]))[1]; // #1 +char (&f(double(&&)[1]))[2]; // #2 + +void quux() { + // Calls #2, float-integral conversion rather than create zero-sized array + static_assert(sizeof(f({})) == 2); +} + +} // namespace Seven +#endif Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -1223,7 +1223,7 @@ Permit conversions to arrays of unknown bound P0388R4 - No + Clang 13 constinit