From: Nathan Sidwell Date: Fri, 21 May 2021 18:34:23 +0000 (-0700) Subject: [clang] p0388 array list initialization overloads X-Git-Tag: upstream/15.0.7~28860 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=444ec0957c58492ecce0569bb20b7d3ad59d37bb;p=platform%2Fupstream%2Fllvm.git [clang] p0388 array list initialization overloads This is the second part of p0388, dealing with overloads of list initialization to incomplete array types. It extends the handling added in D103088 to permit incomplete arrays. We have to record that the conversion involved an incomplete array, and so (re-add) a bit flag into the standard conversion sequence object. Comparing such conversion sequences requires knowing (a) the number of array elements initialized and (b) whether the initialization is of an incomplete array. This also updates the web page to indicate p0388 is implemented (there is no feature macro). Differential Revision: https://reviews.llvm.org/D103908 --- diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index f16595c..7898a58 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -535,7 +535,10 @@ class Sema; }; /// ConversionKind - The kind of implicit conversion sequence. - unsigned ConversionKind; + unsigned ConversionKind : 31; + + // Whether the initializer list was of an incomplete array. + unsigned InitializerListOfIncompleteArray : 1; /// When initializing an array or std::initializer_list from an /// initializer-list, this is the array or std::initializer_list type being @@ -573,12 +576,16 @@ class Sema; }; ImplicitConversionSequence() - : ConversionKind(Uninitialized), InitializerListContainerType() { + : ConversionKind(Uninitialized), + InitializerListOfIncompleteArray(false), + InitializerListContainerType() { Standard.setAsIdentityConversion(); } ImplicitConversionSequence(const ImplicitConversionSequence &Other) : ConversionKind(Other.ConversionKind), + InitializerListOfIncompleteArray( + Other.InitializerListOfIncompleteArray), InitializerListContainerType(Other.InitializerListContainerType) { switch (ConversionKind) { case Uninitialized: break; @@ -680,8 +687,12 @@ class Sema; bool hasInitializerListContainerType() const { return !InitializerListContainerType.isNull(); } - void setInitializerListContainerType(QualType T) { + void setInitializerListContainerType(QualType T, bool IA) { InitializerListContainerType = T; + InitializerListOfIncompleteArray = IA; + } + bool isInitializerListOfIncompleteArray() const { + return InitializerListOfIncompleteArray; } QualType getInitializerListContainerType() const { assert(hasInitializerListContainerType() && diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index da49084..a6b1146 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5865,14 +5865,14 @@ void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2, // 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; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5c513a4..044e3c4 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -3820,8 +3820,8 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc, // 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,13 +3840,23 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc, if (auto *CAT1 = S.Context.getAsConstantArrayType( ICS1.getInitializerListContainerType())) if (auto *CAT2 = S.Context.getAsConstantArrayType( - ICS2.getInitializerListContainerType())) + ICS2.getInitializerListContainerType())) { if (S.Context.hasSameUnqualifiedType(CAT1->getElementType(), - CAT2->getElementType()) && - CAT1->getSize() != CAT2->getSize()) - return CAT1->getSize().ult(CAT2->getSize()) - ? ImplicitConversionSequence::Better - : ImplicitConversionSequence::Worse; + CAT2->getElementType())) { + // Both to arrays of the same element type + 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; + } + } } if (ICS1.isStandard()) @@ -5004,9 +5014,15 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, 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: @@ -5030,18 +5046,16 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, 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; } } } @@ -5059,22 +5073,21 @@ 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. - QualType InitTy = ToType; - ArrayType const *AT = S.Context.getAsArrayType(ToType); if (AT || S.isStdInitializerList(ToType, &InitTy)) { unsigned e = From->getNumInits(); ImplicitConversionSequence DfltElt; DfltElt.setBad(BadConversionSequence::no_conversion, QualType(), QualType()); + QualType ContTy = ToType; + bool IsUnbounded = false; 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)) { // Too many inits, fatally bad Result.setBad(BadConversionSequence::too_many_initializers, From, ToType); - Result.setInitializerListContainerType(ToType); + Result.setInitializerListContainerType(ContTy, IsUnbounded); return Result; } if (CT->getSize().ugt(e)) { @@ -5089,10 +5102,23 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, // No {} init, fatally bad Result.setBad(BadConversionSequence::too_few_initializers, From, ToType); - Result.setInitializerListContainerType(ToType); + Result.setInitializerListContainerType(ContTy, IsUnbounded); return Result; } } + } else { + assert(isa(AT) && "Expected incomplete array"); + IsUnbounded = true; + if (!e) { + // Cannot convert to zero-sized. + Result.setBad(BadConversionSequence::too_few_initializers, From, + ToType); + Result.setInitializerListContainerType(ContTy, IsUnbounded); + return Result; + } + llvm::APInt Size(S.Context.getTypeSize(S.Context.getSizeType()), e); + ContTy = S.Context.getConstantArrayType(InitTy, Size, nullptr, + ArrayType::Normal, 0); } } @@ -5115,7 +5141,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, Result = ICS; // Bail as soon as we find something unconvertible. if (Result.isBad()) { - Result.setInitializerListContainerType(ToType); + Result.setInitializerListContainerType(ContTy, IsUnbounded); return Result; } } @@ -5128,8 +5154,8 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType, S, From->getEndLoc(), DfltElt, Result) == ImplicitConversionSequence::Worse) Result = DfltElt; - - Result.setInitializerListContainerType(ToType); + // Record the type being initialized so that we may compare sequences + Result.setInitializerListContainerType(ContTy, IsUnbounded); return Result; } diff --git a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp index 34f0869..f2d5cab 100644 --- a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp +++ b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -77,3 +77,97 @@ auto *frob2(Mat *(*arp)[1]) { } } // 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{{too many initializers}} + // expected-note@-6{{too many initializers}} + // expected-note@-6{{too many initializers}} +#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 + +namespace Eight { + +// brace-elision is not a thing here: +struct A { + int x, y; +}; + +char (&f1(int(&&)[]))[1]; // #1 +char (&f1(A(&&)[]))[2]; // #2 + +void g1() { + // pick #1, even though that is more elements than #2 + // 6 ints, as opposed to 3 As + static_assert(sizeof(f1({1, 2, 3, 4, 5, 6})) == 1); +} + +void f2(A(&&)[]); // expected-note{{candidate function not viable}} +void g2() { + f2({1, 2, 3, 4, 5, 6}); // expected-error{{no matching function}} +} + +void f3(A(&&)[]); +void g3() { + auto &f = f3; + + f({1, 2, 3, 4, 5, 6}); // OK! We're coercing to an already-selected function +} + +} // namespace Eight + +#endif diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2be1fb4..4b628e1f 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1238,7 +1238,7 @@ code. This issue is expected to be rectified soon. Permit conversions to arrays of unknown bound P0388R4 - No + Clang 13 constinit