[clang] bb3f5f5 - [clang] Array list initialization (pre-p0388)
Nathan Sidwell via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 9 08:30:11 PDT 2021
Author: Nathan Sidwell
Date: 2021-09-09T08:30:04-07:00
New Revision: bb3f5f5d788dd9375ab260f77612fab4a707a1ac
URL: https://github.com/llvm/llvm-project/commit/bb3f5f5d788dd9375ab260f77612fab4a707a1ac
DIFF: https://github.com/llvm/llvm-project/commit/bb3f5f5d788dd9375ab260f77612fab4a707a1ac.diff
LOG: [clang] Array list initialization (pre-p0388)
Extends handling of list initialization of bounded array parameters.
This adds the missing checks on converting each initializer for both
std::initializer_list and arrays. And extends
CompareImplicitConversionSequence to compares array size, for two
conversions to array type.
As noted in this patch, there's a defect in the std concerning the
partial orderability of conversion sequences. DR2492 has a suggested
direction that will be simple to add once it (hopefully) is accepted.
Differential Revision: https://reviews.llvm.org/D103088
Added:
clang/test/SemaCXX/overload-ary-bind.cpp
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Overload.h
clang/lib/Sema/SemaOverload.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f8e89549f0503..824d9bf469360 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4483,7 +4483,8 @@ def note_ovl_candidate_bad_conv_incomplete : Note<
"; remove &}7">;
def note_ovl_candidate_bad_list_argument : Note<
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
- "cannot convert initializer list argument to %4">;
+ "%select{cannot convert initializer list|too few initializers in list"
+ "|too many initializers in list}7 argument to %4">;
def note_ovl_candidate_bad_overload : Note<
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
"no overload of %4 matching %3 for %ordinal5 argument">;
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 82661cb3d12ac..f16595c3319cd 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -469,7 +469,9 @@ class Sema;
unrelated_class,
bad_qualifiers,
lvalue_ref_to_rvalue,
- rvalue_ref_to_lvalue
+ rvalue_ref_to_lvalue,
+ too_few_initializers,
+ too_many_initializers,
};
// This can be null, e.g. for implicit object arguments.
@@ -533,11 +535,14 @@ class Sema;
};
/// 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;
+ /// When initializing an array or std::initializer_list from an
+ /// initializer-list, this is the array or std::initializer_list type being
+ /// initialized. The remainder of the conversion sequence, including ToType,
+ /// describe the worst conversion of an initializer to an element of the
+ /// array or std::initializer_list. (Note, 'worst' is not well defined.)
+ QualType InitializerListContainerType;
void setKind(Kind K) {
destruct();
@@ -568,13 +573,13 @@ class Sema;
};
ImplicitConversionSequence()
- : ConversionKind(Uninitialized), StdInitializerListElement(false) {
+ : ConversionKind(Uninitialized), InitializerListContainerType() {
Standard.setAsIdentityConversion();
}
ImplicitConversionSequence(const ImplicitConversionSequence &Other)
: ConversionKind(Other.ConversionKind),
- StdInitializerListElement(Other.StdInitializerListElement) {
+ InitializerListContainerType(Other.InitializerListContainerType) {
switch (ConversionKind) {
case Uninitialized: break;
case StandardConversion: Standard = Other.Standard; break;
@@ -670,14 +675,18 @@ class Sema;
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;
+ // True iff this is a conversion sequence from an initializer list to an
+ // array or std::initializer.
+ bool hasInitializerListContainerType() const {
+ return !InitializerListContainerType.isNull();
}
-
- void setStdInitializerListElement(bool V = true) {
- StdInitializerListElement = V;
+ void setInitializerListContainerType(QualType T) {
+ InitializerListContainerType = T;
+ }
+ QualType getInitializerListContainerType() const {
+ assert(hasInitializerListContainerType() &&
+ "not initializer list container");
+ return InitializerListContainerType;
}
/// Form an "implicit" conversion sequence from nullptr_t to bool, for a
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 07f50400989e8..4b1b61a390276 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -541,8 +541,8 @@ void UserDefinedConversionSequence::dump() const {
/// 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 (hasInitializerListContainerType())
+ OS << "Worst list element conversion: ";
switch (ConversionKind) {
case StandardConversion:
OS << "Standard conversion: ";
@@ -3777,7 +3777,9 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc,
if (S.getLangOpts().CPlusPlus11 && !S.getLangOpts().WritableStrings &&
hasDeprecatedStringLiteralToCharPtrConversion(ICS1) !=
- hasDeprecatedStringLiteralToCharPtrConversion(ICS2))
+ hasDeprecatedStringLiteralToCharPtrConversion(ICS2) &&
+ // Ill-formedness must not
diff er
+ ICS1.isBad() == ICS2.isBad())
return hasDeprecatedStringLiteralToCharPtrConversion(ICS1)
? ImplicitConversionSequence::Worse
: ImplicitConversionSequence::Better;
@@ -3803,16 +3805,35 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc,
// list-initialization sequence L2 if:
// - L1 converts to std::initializer_list<X> 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())
- return ImplicitConversionSequence::Better;
- if (!ICS1.isStdInitializerListElement() &&
- ICS2.isStdInitializerListElement())
- return ImplicitConversionSequence::Worse;
+ bool StdInit1 = false, StdInit2 = false;
+ if (ICS1.hasInitializerListContainerType())
+ StdInit1 = S.isStdInitializerList(ICS1.getInitializerListContainerType(),
+ nullptr);
+ if (ICS2.hasInitializerListContainerType())
+ StdInit2 = S.isStdInitializerList(ICS2.getInitializerListContainerType(),
+ nullptr);
+ if (StdInit1 != StdInit2)
+ return StdInit1 ? ImplicitConversionSequence::Better
+ : ImplicitConversionSequence::Worse;
+
+ if (ICS1.hasInitializerListContainerType() &&
+ ICS2.hasInitializerListContainerType())
+ if (auto *CAT1 = S.Context.getAsConstantArrayType(
+ ICS1.getInitializerListContainerType()))
+ if (auto *CAT2 = S.Context.getAsConstantArrayType(
+ ICS2.getInitializerListContainerType()))
+ if (S.Context.hasSameUnqualifiedType(CAT1->getElementType(),
+ CAT2->getElementType()) &&
+ CAT1->getSize() != CAT2->getSize())
+ return CAT1->getSize().ult(CAT2->getSize())
+ ? ImplicitConversionSequence::Better
+ : ImplicitConversionSequence::Worse;
}
if (ICS1.isStandard())
@@ -5066,43 +5087,77 @@ 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.
- //
- // 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) {
+ 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());
+ 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);
+ return Result;
+ }
+ if (CT->getSize().ugt(e)) {
+ // Need an init from empty {}, is there one?
+ InitListExpr EmptyList(S.Context, From->getEndLoc(), None,
+ From->getEndLoc());
+ EmptyList.setType(S.Context.VoidTy);
+ DfltElt = TryListConversion(
+ S, &EmptyList, InitTy, SuppressUserConversions,
+ InOverloadResolution, AllowObjCWritebackConversion);
+ if (DfltElt.isBad()) {
+ // No {} init, fatally bad
+ Result.setBad(BadConversionSequence::too_few_initializers, From,
+ ToType);
+ Result.setInitializerListContainerType(ToType);
+ return Result;
+ }
+ }
+ }
+ }
+
+ Result.setStandard();
+ Result.Standard.setAsIdentityConversion();
+ Result.Standard.setFromType(InitTy);
+ Result.Standard.setAllToTypes(InitTy);
+ for (unsigned i = 0; 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()) {
+ ImplicitConversionSequence ICS = TryCopyInitialization(
+ S, Init, InitTy, SuppressUserConversions, InOverloadResolution,
+ AllowObjCWritebackConversion);
+
+ // Keep the worse conversion seen so far.
+ // FIXME: Sequences are not totally ordered, so 'worse' can be
+ // ambiguous. CWG has been informed.
+ if (CompareImplicitConversionSequences(S, From->getBeginLoc(), ICS,
+ Result) ==
+ ImplicitConversionSequence::Worse) {
Result = ICS;
- break;
+ // Bail as soon as we find something unconvertible.
+ if (Result.isBad()) {
+ Result.setInitializerListContainerType(ToType);
+ return Result;
+ }
}
- // 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) {
- Result.setStandard();
- Result.Standard.setAsIdentityConversion();
- Result.Standard.setFromType(ToType);
- Result.Standard.setAllToTypes(ToType);
- }
+ // If we needed any implicit {} initialization, compare that now.
+ // over.ics.list/6 indicates we should compare that conversion. Again CWG
+ // has been informed that this might not be the best thing.
+ if (!DfltElt.isBad() && CompareImplicitConversionSequences(
+ S, From->getEndLoc(), DfltElt, Result) ==
+ ImplicitConversionSequence::Worse)
+ Result = DfltElt;
- Result.setStdInitializerListElement(toStdInitializerList);
+ Result.setInitializerListContainerType(ToType);
return Result;
}
@@ -5481,6 +5536,10 @@ Sema::PerformObjectArgumentInitialization(Expr *From,
case BadConversionSequence::no_conversion:
case BadConversionSequence::unrelated_class:
break;
+
+ case BadConversionSequence::too_few_initializers:
+ case BadConversionSequence::too_many_initializers:
+ llvm_unreachable("Lists are not objects");
}
return Diag(From->getBeginLoc(), diag::err_member_function_call_bad_type)
@@ -10528,7 +10587,11 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_list_argument)
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
<< (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
- << ToTy << (unsigned)isObjectArgument << I + 1;
+ << ToTy << (unsigned)isObjectArgument << I + 1
+ << (Conv.Bad.Kind == BadConversionSequence::too_few_initializers ? 1
+ : Conv.Bad.Kind == BadConversionSequence::too_many_initializers
+ ? 2
+ : 0);
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
diff --git a/clang/test/SemaCXX/overload-ary-bind.cpp b/clang/test/SemaCXX/overload-ary-bind.cpp
new file mode 100644
index 0000000000000..824ec50e87bcd
--- /dev/null
+++ b/clang/test/SemaCXX/overload-ary-bind.cpp
@@ -0,0 +1,97 @@
+// 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{{too many initializers}}
+char (&b(int(&&)[2]))[2]; // #2 expected-note{{too many initializers}}
+
+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{{too few initializers}}
+
+void f() {
+ b({2}); // #1 expected-error{{no matching function}}
+}
+} // namespace Three
+
+namespace Four {
+char (&b(int(&&)[1], float))[1]; // #1 expected-note{{candidate}}
+char (&b(int(&&)[1], double))[2]; // #2 expected-note{{candidate}}
+
+char (&c(float, int(&&)[1]))[1]; // #1 expected-note{{candidate}}
+char (&c(double, int(&&)[1]))[2]; // #2 expected-note{{candidate}}
+
+void f() {
+ b({1}, 0); // expected-error{{is ambiguous}}
+ c(0, {1}); // expected-error{{is ambiguous}}
+}
+} // namespace Four
+
+typedef decltype(sizeof(char)) size_t;
+namespace std {
+// sufficient initializer list
+template <class _E>
+class initializer_list {
+ const _E *__begin_;
+ size_t __size_;
+
+ constexpr initializer_list(const _E *__b, size_t __s)
+ : __begin_(__b),
+ __size_(__s) {}
+
+public:
+ typedef _E value_type;
+ typedef const _E &reference;
+ typedef const _E &const_reference;
+ typedef size_t size_type;
+
+ typedef const _E *iterator;
+ typedef const _E *const_iterator;
+
+ constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
+
+ constexpr size_t size() const { return __size_; }
+ constexpr const _E *begin() const { return __begin_; }
+ constexpr const _E *end() const { return __begin_ + __size_; }
+};
+} // namespace std
+
+namespace Five {
+struct ugly {
+ ugly(char *);
+ ugly(int);
+};
+char (&f(std::initializer_list<char *>))[1]; // #1
+char (&f(std::initializer_list<ugly>))[2]; // #2
+void g() {
+ // Pick #2 as #1 not viable (3->char * fails).
+ static_assert(sizeof(f({"hello", 3})) == 2); // expected-warning{{not allow}}
+}
+
+} // namespace Five
More information about the cfe-commits
mailing list