};
/// 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
};
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;
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() &&
// 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;
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())
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:
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;
}
}
}
// 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)) {
// 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);
}
}
Result = ICS;
// Bail as soon as we find something unconvertible.
if (Result.isBad()) {
- Result.setInitializerListContainerType(ToType);
+ Result.setInitializerListContainerType(ContTy, IsUnbounded);
return Result;
}
}
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;
}
}
} // 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