[clang] [Clang] Copy initialization of an object of the same type should not consider user-defined conversions (PR #185936)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 03:05:19 PDT 2026
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/185936
>From e98a199a734f6fde6e6d7e813931ced2266d1770 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 11 Mar 2026 18:15:23 +0100
Subject: [PATCH 1/3] [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{});
+}
+}
>From 6fd14829bb062f548d2c06a527a1582f10a03608 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 11 Mar 2026 18:40:19 +0100
Subject: [PATCH 2/3] address feedback
---
clang/docs/ReleaseNotes.rst | 3 +--
clang/lib/Sema/SemaInit.cpp | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7826575f2b68e..d9d0fd6308d95 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -354,9 +354,8 @@ 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).
+ conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e482fd9fb4500..ede2b9beef49b 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4692,7 +4692,7 @@ 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
+ // constructors are considered.
if ((Kind.getKind() == InitializationKind::IK_Direct ||
Kind.getKind() == InitializationKind::IK_Copy) &&
Args.size() == 1 &&
>From 2dcf4b65a296405df31b0c32575d7806ea27c8f2 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 12 Mar 2026 11:05:03 +0100
Subject: [PATCH 3/3] add test
---
clang/test/SemaCXX/copy-initialization.cpp | 26 +++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/copy-initialization.cpp b/clang/test/SemaCXX/copy-initialization.cpp
index 6fbf980541b3d..68ccc3014e8b6 100644
--- a/clang/test/SemaCXX/copy-initialization.cpp
+++ b/clang/test/SemaCXX/copy-initialization.cpp
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
class X {
public:
@@ -80,3 +81,26 @@ struct B : A {
B(int);
};
B b = 0; // ok, calls B(int) then A(const A&) then B(A).
+
+
+namespace GH149443 {
+#if __cplusplus >= 202002
+template <class... T>
+concept C = [] {
+ static_assert(sizeof...(T) > 1);
+ return true;
+}();
+
+struct Deferred {
+ template <typename TO>
+ operator TO();
+};
+
+void foo(Deferred);
+
+void bar(Deferred d) {
+ foo(d);
+}
+
+#endif
+}
More information about the cfe-commits
mailing list