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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits