https://github.com/cor3ntin created 
https://github.com/llvm/llvm-project/pull/185936

We were incorrectly considering conversion operators during overload resolution 
when selecting the constructor of a copy initialization.

This is hard to observed as such conversion always have a lower rank anyway so 
they were discarded later.

However it led to recursive concept checking.

Fixes #149443

>From e98a199a734f6fde6e6d7e813931ced2266d1770 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <[email protected]>
Date: Wed, 11 Mar 2026 18:15:23 +0100
Subject: [PATCH] [Clang] Copy initialization of an object of the same type
 should not consider user-defined conversions.

We were incorrectly considering conversion operators during overload resolution
when selecting the constructor of a copy initialization.

This is hard to observed as such conversion always have a lower rank anyway
so they were discarded later.

However it led to recursive concept checking.

Fixes #149443
---
 clang/docs/ReleaseNotes.rst                   |  3 ++
 clang/lib/Sema/SemaInit.cpp                   | 12 +++++
 .../SemaTemplate/concepts-recursive-inst.cpp  | 46 +++++++++++--------
 3 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3617786f09595..7826575f2b68e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -355,6 +355,9 @@ Bug Fixes to C++ Support
 - Fix initialization of GRO when GRO-return type mismatches, as part of 
CWG2563. (#GH98744)
 - Fix an error using an initializer list with array new for a type that is not 
default-constructible. (#GH81157)
 
+- We no longer consider conversion operators when copy-initializing from the 
same type. This was non
+  conforming and could lead to recursive constraint satisfaction (#GH149443).
+
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed a bug where explicit nullability property attributes were not stored 
in AST nodes in Objective-C. (#GH179703)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 48be03bb2f0f8..e482fd9fb4500 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4689,6 +4689,18 @@ static void TryConstructorInitialization(Sema &S,
     }
   }
 
+  // if the initialization is direct-initialization, or if it is
+  // copy-initialization where the cv-unqualified version of the source type is
+  // the same as or is derived from the class of the destination type,
+  // constructors are considered. The
+  if ((Kind.getKind() == InitializationKind::IK_Direct ||
+       Kind.getKind() == InitializationKind::IK_Copy) &&
+      Args.size() == 1 &&
+      S.getASTContext().hasSameUnqualifiedType(
+          Args[0]->getType().getNonReferenceType(),
+          DestType.getNonReferenceType()))
+    RequireActualConstructor = true;
+
   // C++11 [over.match.list]p1:
   //   - If no viable initializer-list constructor is found, overload 
resolution
   //     is performed again, where the candidate functions are all the
diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp 
b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
index 5e1bce5f15684..f270e7b4e7912 100644
--- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp
+++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
@@ -87,30 +87,16 @@ auto it = begin(rng); // #BEGIN_CALL
 
 namespace GH50891 {
   template <typename T>
-  concept Numeric = requires(T a) { // #NUMERIC
-      foo(a); // #FOO_CALL
+  concept Numeric = requires(T a) {
+      foo(a);
     };
 
   struct Deferred {
     friend void foo(Deferred);
-    template <Numeric TO> operator TO(); // #OP_TO
+    template <Numeric TO> operator TO();
   };
 
-  static_assert(Numeric<Deferred>); // #STATIC_ASSERT
-  // expected-error@#NUMERIC{{satisfaction of constraint 'requires (T a) { 
foo(a); }' depends on itself}}
-  // expected-note@#NUMERIC {{while substituting template arguments into 
constraint expression here}}
-  // expected-note@#OP_TO {{while checking the satisfaction of concept 
'Numeric<Deferred>' requested here}}
-  // expected-note@#OP_TO {{skipping 1 context}}
-  // expected-note@#FOO_CALL 2{{while checking constraint satisfaction for 
template}}
-  // expected-note@#FOO_CALL 2{{while substituting deduced template arguments 
into function template}}
-  // expected-note@#FOO_CALL 2{{in instantiation of requirement here}}
-  // expected-note@#NUMERIC {{while substituting template arguments into 
constraint expression here}}
-
-  // expected-error@#STATIC_ASSERT {{static assertion failed}}
-  // expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 
'Numeric<Deferred>' requested here}}
-  // expected-note@#STATIC_ASSERT{{because 'Deferred' does not satisfy 
'Numeric'}}
-  // expected-note@#FOO_CALL{{because 'foo(a)' would be invalid}}
-
+  static_assert(Numeric<Deferred>);
 } // namespace GH50891
 
 
@@ -362,3 +348,27 @@ static_assert(StreamCanReceiveString<std::stringstream>);
 #endif
 }
 }
+
+
+namespace GH149443 {
+template<class T>
+concept can_simply_copy_construct = requires (const T& x) {
+  T(x);
+};
+
+struct A {
+  template <can_simply_copy_construct T>
+  operator T() const noexcept {
+    return T{};
+  }
+};
+
+template<can_simply_copy_construct T1, can_simply_copy_construct T0>
+T1 f(T0 t) {
+  return t;
+}
+
+int main() {
+  (void) f<int>(A{});
+}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to