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
