[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
This commit is contained in:
parent
dcd74716f9
commit
444ec0957c
|
@ -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() &&
|
||||
|
|
|
@ -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<ConstantArrayType>(AT1)) {
|
||||
auto *CAT2 = dyn_cast<ConstantArrayType>(AT2);
|
||||
if (!(CAT2 && CAT1->getSize() == CAT2->getSize()) &&
|
||||
!(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
|
||||
isa<IncompleteArrayType>(AT2)))
|
||||
if (!((CAT2 && CAT1->getSize() == CAT2->getSize()) ||
|
||||
(AllowPiMismatch && getLangOpts().CPlusPlus20 &&
|
||||
isa<IncompleteArrayType>(AT2))))
|
||||
return;
|
||||
} else if (isa<IncompleteArrayType>(AT1)) {
|
||||
if (!isa<IncompleteArrayType>(AT2) &&
|
||||
!(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
|
||||
isa<ConstantArrayType>(AT2)))
|
||||
if (!(isa<IncompleteArrayType>(AT2) ||
|
||||
(AllowPiMismatch && getLangOpts().CPlusPlus20 &&
|
||||
isa<ConstantArrayType>(AT2))))
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
|
|
|
@ -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<IncompleteArrayType>(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<ConstantArrayType>(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<IncompleteArrayType>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1238,7 +1238,7 @@ code. This issue is expected to be rectified soon.
|
|||
<tr>
|
||||
<td>Permit conversions to arrays of unknown bound</td>
|
||||
<td><a href="https://wg21.link/p0388r4">P0388R4</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="unreleased" align="center">Clang 13</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt>constinit</tt></td>
|
||||
|
|
Loading…
Reference in New Issue