[clang] 614b860 - [Clang] Copy initialization of an object of the same type should not consider user-defined conversions (#185936)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 09:35:41 PDT 2026
Author: Corentin Jabot
Date: 2026-03-12T17:35:36+01:00
New Revision: 614b860af084a3afc94ac9c9467d6e847585f455
URL: https://github.com/llvm/llvm-project/commit/614b860af084a3afc94ac9c9467d6e847585f455
DIFF: https://github.com/llvm/llvm-project/commit/614b860af084a3afc94ac9c9467d6e847585f455.diff
LOG: [Clang] Copy initialization of an object of the same type should not consider user-defined conversions (#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
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaInit.cpp
clang/test/SemaCXX/copy-initialization.cpp
clang/test/SemaTemplate/concepts-recursive-inst.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3617786f09595..d9d0fd6308d95 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -354,6 +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 checking. (#GH149443)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 48be03bb2f0f8..ede2b9beef49b 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.
+ 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/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
+}
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{});
+}
+}
More information about the cfe-commits
mailing list