https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153
--- Comment #11 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Tomasz Kaminski <[email protected]>: https://gcc.gnu.org/g:8fad43b7850a99b32c48570fc2a3d8ae5a76542a commit r16-6898-g8fad43b7850a99b32c48570fc2a3d8ae5a76542a Author: Tomasz KamiÅski <[email protected]> Date: Fri Jan 16 14:01:53 2026 +0100 libstdc++: Use overload operator<=> when provided in relational functors [PR114153] The implementation of less<> did not consider the possibility of t < u being rewritten from overloaded operator<=>. This lead to situation when for t,u that: * provide overload operator<=>, such that (t < u) is rewritten to (t <=> u) < 0, * are convertible to pointers, the expression std::less<>(t, u) would incorrectly result in call of std::less<void*> on values converted to the pointers, instead of t < u. The similar issues also occurred for greater<>, less_equal<>, greater_equal<>, their range equivalents, and in three_way_compare for heterogeneous calls. This patch addresses above, by also checking for free-functions and member overloads of operator<=>, before falling back to pointer comparison. We do not put any constraints on the return type of selected operator, in particular in being one of the standard defined comparison categories, as the language does not put any restriction of returned type, and if (t <=> u) is well formed, (t op u) is interpreted as (t <=> u) op 0. If that later expression is ill-formed, the expression using op also is (see included tests). The relational operator rewrites try both order of arguments, t < u, can be rewritten into operator<=>(t, u) < 0 or 0 < operator<=>(u, t), it means that we need to test both operator<=>(T, U) and operator<=>(U, T) if T and U are not the same types. This is now extracted into __not_overloaded_spaceship helper concept, placed in <concepts>, to avoid extending set of includes. The compare_three_way functor defined in compare, already considers overloaded operator<=>, however it does not consider reversed candidates, leading to situation in which t <=> u results in 0 <=> operator<=>(u, t), while compare_three_way{}(t, u) uses pointer comparison. This is also addressed by using __not_overloaded_spaceship, that check both order of arguments. Finally, as operator<=> is introduced in C++20, for std::less(_equal)?<>, std::greater(_equal)?<>, we use provide separate __ptr_cmp implementation in that mode, that relies on use of requires expression. We use a nested requires clause to guarantee short-circuiting of their evaluation. The operator() of aforementioned functors is reworked to use if constexpr, in all standard modes (as we allow is as extension), eliminating the need for _S_cmp function. PR libstdc++/114153 libstdc++-v3/ChangeLog: * include/bits/ranges_cmp.h (__detail::__less_builtin_ptr_cmp): Add __not_overloaded_spaceship spaceship check. * include/bits/stl_function.h (greater<void>::operator()) (less<void>::operator(), greater_equal<void>::operator()) (less_equal<void>::operator()): Implement using if constexpr. (greater<void>::__S_cmp, less<void>::__S_cmp) (greater_equal<void>::__ptr_comp, less_equal<void>::S_cmp): Remove. (greater<void>::__ptr_cmp, less<void>::__ptr_cmp) (greater_equal<void>::__ptr_comp, less_equal<void>::ptr_cmp): Change tostatic constexpr variable. Define in terms of requires expressions and __not_overloaded_spaceship check. * include/std/concepts: (__detail::__not_overloaded_spaceship): Define. * libsupc++/compare: (__detail::__3way_builtin_ptr_cmp): Use __not_overloaded_spaceship concept. * testsuite/20_util/function_objects/comparisons_pointer_spaceship.cc: New test. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz KamiÅski <[email protected]>
