[clang] cce6de8 - [Clang] Never consider conversion from single-element braced-init-list perfect (#138307)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 2 11:56:05 PDT 2025
Author: cor3ntin
Date: 2025-05-02T20:56:02+02:00
New Revision: cce6de83138223db07d538824e0c398d5da83190
URL: https://github.com/llvm/llvm-project/commit/cce6de83138223db07d538824e0c398d5da83190
DIFF: https://github.com/llvm/llvm-project/commit/cce6de83138223db07d538824e0c398d5da83190.diff
LOG: [Clang] Never consider conversion from single-element braced-init-list perfect (#138307)
We might prefer a template std::initializer list constructor.
Fix a regression introduced by #136203
https://github.com/llvm/llvm-project/pull/136203#issuecomment-2843498895
GCC had a similar issue and a similar fix
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100963
Added:
Modified:
clang/include/clang/Sema/Overload.h
clang/lib/Sema/SemaOverload.cpp
clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 8182ce9c39685..5c155fb1dbebf 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -360,6 +360,13 @@ class Sema;
LLVM_PREFERRED_TYPE(bool)
unsigned ObjCLifetimeConversionBinding : 1;
+ /// Whether the source expression was originally a single element
+ /// braced-init-list. Such a conversion is not a perfect match,
+ /// as we prefer a std::list_initializer constructor over an exact match
+ /// constructor.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned FromBracedInitList : 1;
+
/// FromType - The type that this conversion is converting
/// from. This is an opaque pointer that can be translated into a
/// QualType.
@@ -412,6 +419,12 @@ class Sema;
bool isPerfect(const ASTContext &C) const {
if (!isIdentityConversion())
return false;
+
+ // We might prefer a std::initializer constructor,
+ // so this sequence cannot be perfect
+ if (FromBracedInitList)
+ return false;
+
// If we are not performing a reference binding, we can skip comparing
// the types, which has a noticeable performance impact.
if (!ReferenceBinding) {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 45415b71ee65c..f7ec4081ce8be 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -246,6 +246,7 @@ void StandardConversionSequence::setAsIdentityConversion() {
BindsToRvalue = false;
BindsImplicitObjectArgumentWithoutRefQualifier = false;
ObjCLifetimeConversionBinding = false;
+ FromBracedInitList = false;
CopyConstructor = nullptr;
}
@@ -1692,12 +1693,14 @@ TryUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
// has a single element of type cv U, where U is X or a class derived
// from X, the implicit conversion sequence has Exact Match rank if U is
// X, or Conversion rank if U is derived from X.
+ bool FromListInit = false;
if (const auto *InitList = dyn_cast<InitListExpr>(From);
InitList && InitList->getNumInits() == 1 &&
!S.isInitListConstructor(Constructor)) {
const Expr *SingleInit = InitList->getInit(0);
FromType = SingleInit->getType();
FromLoc = SingleInit->getBeginLoc();
+ FromListInit = true;
} else {
FromType = From->getType();
FromLoc = From->getBeginLoc();
@@ -1715,6 +1718,7 @@ TryUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
ICS.Standard.setAsIdentityConversion();
ICS.Standard.setFromType(FromType);
ICS.Standard.setAllToTypes(ToType);
+ ICS.Standard.FromBracedInitList = FromListInit;
ICS.Standard.CopyConstructor = Constructor;
ICS.Standard.FoundCopyConstructor = Found;
if (ToCanon != FromCanon)
@@ -4062,6 +4066,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
if (isa<InitListExpr>(From)) {
// Initializer lists don't have conversions as such.
User.Before.setAsIdentityConversion();
+ User.Before.FromBracedInitList = true;
} else {
if (Best->Conversions[0].isEllipsis())
User.EllipsisConversion = true;
@@ -5276,6 +5281,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
ICS.Standard.ObjCLifetimeConversionBinding =
(RefConv & Sema::ReferenceConversions::ObjCLifetime) != 0;
+ ICS.Standard.FromBracedInitList = false;
ICS.Standard.CopyConstructor = nullptr;
ICS.Standard.DeprecatedStringLiteralToCharPtr = false;
};
@@ -5474,6 +5480,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
ICS.UserDefined.After.BindsToRvalue = !LValRefType;
ICS.UserDefined.After.BindsImplicitObjectArgumentWithoutRefQualifier = false;
ICS.UserDefined.After.ObjCLifetimeConversionBinding = false;
+ ICS.UserDefined.After.FromBracedInitList = false;
}
return ICS;
@@ -5760,6 +5767,8 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
SCS.BindsToFunctionLvalue = false;
SCS.BindsImplicitObjectArgumentWithoutRefQualifier = false;
SCS.ObjCLifetimeConversionBinding = false;
+ SCS.FromBracedInitList = false;
+
} else
Result.setBad(BadConversionSequence::lvalue_ref_to_rvalue,
From, ToType);
@@ -5777,10 +5786,13 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// single integer.
unsigned NumInits = From->getNumInits();
if (NumInits == 1 && !isa<InitListExpr>(From->getInit(0)) &&
- !isa<EmbedExpr>(From->getInit(0)))
+ !isa<EmbedExpr>(From->getInit(0))) {
Result = TryCopyInitialization(
S, From->getInit(0), ToType, SuppressUserConversions,
InOverloadResolution, AllowObjCWritebackConversion);
+ if (Result.isStandard())
+ Result.Standard.FromBracedInitList = true;
+ }
// - if the initializer list has no elements, the implicit conversion
// sequence is the identity conversion.
else if (NumInits == 0) {
@@ -5993,6 +6005,7 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
ICS.Standard.IsLvalueReference = Method->getRefQualifier() != RQ_RValue;
ICS.Standard.BindsToFunctionLvalue = false;
ICS.Standard.BindsToRvalue = FromClassification.isRValue();
+ ICS.Standard.FromBracedInitList = false;
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier
= (Method->getRefQualifier() == RQ_None);
return ICS;
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index d68a942f64050..277c5df3bb62b 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -2,6 +2,20 @@
// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++20 %s
// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -fsyntax-only -verify -std=c++2c %s
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+ 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:
+ constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
+ };
+} // namespace std
+
template <typename T>
struct Invalid { static_assert(false, "instantiated Invalid"); }; // #err-invalid
@@ -204,3 +218,17 @@ using a = void(int &);
template <typename c> void d(c &);
void f(a);
template <class> void f(bool j) { f(&d<int>); }
+
+struct InitListAreNotPerfect {
+ InitListAreNotPerfect(int) = delete;
+ template<class T>
+ InitListAreNotPerfect(std::initializer_list<T>);
+};
+InitListAreNotPerfect InitListAreNotPerfect_test({0});
+struct InitListAreNotPerfectCpy {
+ InitListAreNotPerfectCpy();
+ InitListAreNotPerfectCpy(const InitListAreNotPerfectCpy&);
+ template <typename T> InitListAreNotPerfectCpy(std::initializer_list<T>);
+};
+
+InitListAreNotPerfectCpy InitListAreNotPerfectCpy_test({InitListAreNotPerfectCpy{}});
More information about the cfe-commits
mailing list