[clang] [Clang] Never consider conversion from single-element braced-init-list perfect (PR #138307)

via cfe-commits cfe-commits at lists.llvm.org
Fri May 2 09:37:41 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/138307.diff


3 Files Affected:

- (modified) clang/include/clang/Sema/Overload.h (+13) 
- (modified) clang/lib/Sema/SemaOverload.cpp (+14-1) 
- (modified) clang/test/SemaCXX/overload-resolution-deferred-templates.cpp (+28) 


``````````diff
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 de2382ffdf1e8..312bb29d49cc3 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{}});

``````````

</details>


https://github.com/llvm/llvm-project/pull/138307


More information about the cfe-commits mailing list