Skip to content

Commit 9aef367

Browse files
cor3ntinIanWood1
authored andcommitted
[Clang] Never consider conversion from single-element braced-init-list perfect (llvm#138307)
We might prefer a template std::initializer list constructor. Fix a regression introduced by llvm#136203 llvm#136203 (comment) GCC had a similar issue and a similar fix https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100963
1 parent 50afa1e commit 9aef367

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

clang/include/clang/Sema/Overload.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,13 @@ class Sema;
360360
LLVM_PREFERRED_TYPE(bool)
361361
unsigned ObjCLifetimeConversionBinding : 1;
362362

363+
/// Whether the source expression was originally a single element
364+
/// braced-init-list. Such a conversion is not a perfect match,
365+
/// as we prefer a std::list_initializer constructor over an exact match
366+
/// constructor.
367+
LLVM_PREFERRED_TYPE(bool)
368+
unsigned FromBracedInitList : 1;
369+
363370
/// FromType - The type that this conversion is converting
364371
/// from. This is an opaque pointer that can be translated into a
365372
/// QualType.
@@ -412,6 +419,12 @@ class Sema;
412419
bool isPerfect(const ASTContext &C) const {
413420
if (!isIdentityConversion())
414421
return false;
422+
423+
// We might prefer a std::initializer constructor,
424+
// so this sequence cannot be perfect
425+
if (FromBracedInitList)
426+
return false;
427+
415428
// If we are not performing a reference binding, we can skip comparing
416429
// the types, which has a noticeable performance impact.
417430
if (!ReferenceBinding) {

clang/lib/Sema/SemaOverload.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ void StandardConversionSequence::setAsIdentityConversion() {
246246
BindsToRvalue = false;
247247
BindsImplicitObjectArgumentWithoutRefQualifier = false;
248248
ObjCLifetimeConversionBinding = false;
249+
FromBracedInitList = false;
249250
CopyConstructor = nullptr;
250251
}
251252

@@ -1692,12 +1693,14 @@ TryUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
16921693
// has a single element of type cv U, where U is X or a class derived
16931694
// from X, the implicit conversion sequence has Exact Match rank if U is
16941695
// X, or Conversion rank if U is derived from X.
1696+
bool FromListInit = false;
16951697
if (const auto *InitList = dyn_cast<InitListExpr>(From);
16961698
InitList && InitList->getNumInits() == 1 &&
16971699
!S.isInitListConstructor(Constructor)) {
16981700
const Expr *SingleInit = InitList->getInit(0);
16991701
FromType = SingleInit->getType();
17001702
FromLoc = SingleInit->getBeginLoc();
1703+
FromListInit = true;
17011704
} else {
17021705
FromType = From->getType();
17031706
FromLoc = From->getBeginLoc();
@@ -1715,6 +1718,7 @@ TryUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
17151718
ICS.Standard.setAsIdentityConversion();
17161719
ICS.Standard.setFromType(FromType);
17171720
ICS.Standard.setAllToTypes(ToType);
1721+
ICS.Standard.FromBracedInitList = FromListInit;
17181722
ICS.Standard.CopyConstructor = Constructor;
17191723
ICS.Standard.FoundCopyConstructor = Found;
17201724
if (ToCanon != FromCanon)
@@ -4062,6 +4066,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
40624066
if (isa<InitListExpr>(From)) {
40634067
// Initializer lists don't have conversions as such.
40644068
User.Before.setAsIdentityConversion();
4069+
User.Before.FromBracedInitList = true;
40654070
} else {
40664071
if (Best->Conversions[0].isEllipsis())
40674072
User.EllipsisConversion = true;
@@ -5276,6 +5281,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
52765281
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
52775282
ICS.Standard.ObjCLifetimeConversionBinding =
52785283
(RefConv & Sema::ReferenceConversions::ObjCLifetime) != 0;
5284+
ICS.Standard.FromBracedInitList = false;
52795285
ICS.Standard.CopyConstructor = nullptr;
52805286
ICS.Standard.DeprecatedStringLiteralToCharPtr = false;
52815287
};
@@ -5474,6 +5480,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
54745480
ICS.UserDefined.After.BindsToRvalue = !LValRefType;
54755481
ICS.UserDefined.After.BindsImplicitObjectArgumentWithoutRefQualifier = false;
54765482
ICS.UserDefined.After.ObjCLifetimeConversionBinding = false;
5483+
ICS.UserDefined.After.FromBracedInitList = false;
54775484
}
54785485

54795486
return ICS;
@@ -5760,6 +5767,8 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
57605767
SCS.BindsToFunctionLvalue = false;
57615768
SCS.BindsImplicitObjectArgumentWithoutRefQualifier = false;
57625769
SCS.ObjCLifetimeConversionBinding = false;
5770+
SCS.FromBracedInitList = false;
5771+
57635772
} else
57645773
Result.setBad(BadConversionSequence::lvalue_ref_to_rvalue,
57655774
From, ToType);
@@ -5777,10 +5786,13 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
57775786
// single integer.
57785787
unsigned NumInits = From->getNumInits();
57795788
if (NumInits == 1 && !isa<InitListExpr>(From->getInit(0)) &&
5780-
!isa<EmbedExpr>(From->getInit(0)))
5789+
!isa<EmbedExpr>(From->getInit(0))) {
57815790
Result = TryCopyInitialization(
57825791
S, From->getInit(0), ToType, SuppressUserConversions,
57835792
InOverloadResolution, AllowObjCWritebackConversion);
5793+
if (Result.isStandard())
5794+
Result.Standard.FromBracedInitList = true;
5795+
}
57845796
// - if the initializer list has no elements, the implicit conversion
57855797
// sequence is the identity conversion.
57865798
else if (NumInits == 0) {
@@ -5993,6 +6005,7 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
59936005
ICS.Standard.IsLvalueReference = Method->getRefQualifier() != RQ_RValue;
59946006
ICS.Standard.BindsToFunctionLvalue = false;
59956007
ICS.Standard.BindsToRvalue = FromClassification.isRValue();
6008+
ICS.Standard.FromBracedInitList = false;
59966009
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier
59976010
= (Method->getRefQualifier() == RQ_None);
59986011
return ICS;

clang/test/SemaCXX/overload-resolution-deferred-templates.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++20 %s
33
// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++2c %s
44

5+
namespace std {
6+
typedef decltype(sizeof(int)) size_t;
7+
template <class _E> class initializer_list {
8+
const _E *__begin_;
9+
size_t __size_;
10+
11+
constexpr initializer_list(const _E *__b, size_t __s)
12+
: __begin_(__b), __size_(__s) {}
13+
14+
public:
15+
constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
16+
};
17+
} // namespace std
18+
519
template <typename T>
620
struct Invalid { static_assert(false, "instantiated Invalid"); }; // #err-invalid
721

@@ -204,3 +218,17 @@ using a = void(int &);
204218
template <typename c> void d(c &);
205219
void f(a);
206220
template <class> void f(bool j) { f(&d<int>); }
221+
222+
struct InitListAreNotPerfect {
223+
InitListAreNotPerfect(int) = delete;
224+
template<class T>
225+
InitListAreNotPerfect(std::initializer_list<T>);
226+
};
227+
InitListAreNotPerfect InitListAreNotPerfect_test({0});
228+
struct InitListAreNotPerfectCpy {
229+
InitListAreNotPerfectCpy();
230+
InitListAreNotPerfectCpy(const InitListAreNotPerfectCpy&);
231+
template <typename T> InitListAreNotPerfectCpy(std::initializer_list<T>);
232+
};
233+
234+
InitListAreNotPerfectCpy InitListAreNotPerfectCpy_test({InitListAreNotPerfectCpy{}});

0 commit comments

Comments
 (0)