Skip to content

Conversation

@cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented May 2, 2025

We might prefer a template std::initializer list constructor.

Fix a regression introduced by #136203
#136203 (comment)

GCC had a similar issue and a similar fix
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100963

…t perfect

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
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 2, 2025
@cor3ntin cor3ntin requested review from erichkeane and zyn0217 May 2, 2025 16:37
@llvmbot
Copy link
Member

llvmbot commented May 2, 2025

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

We might prefer a template std::initializer list constructor.

Fix a regression introduced by #136203
#136203 (comment)

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 --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{}});

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cor3ntin cor3ntin merged commit cce6de8 into llvm:main May 2, 2025
12 of 13 checks passed

/// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::initializer_list :)

if (!isIdentityConversion())
return false;

// We might prefer a std::initializer constructor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::initializer_list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oups

cor3ntin added a commit that referenced this pull request May 3, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…t 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
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
…t 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
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants