[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-02-05 Thread tkaminsk at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Tomasz Kamiński  changed:

   What|Removed |Added

 Status|NEW |RESOLVED
 Resolution|--- |FIXED

--- Comment #15 from Tomasz Kamiński  ---
Backported to v14 and v15.

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-02-05 Thread cvs-commit at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #14 from GCC Commits  ---
The releases/gcc-14 branch has been updated by Tomasz Kaminski
:

https://gcc.gnu.org/g:11e8abd241128cf23b1da1eb5ce6db4cfefa55e9

commit r14-12298-g11e8abd241128cf23b1da1eb5ce6db4cfefa55e9
Author: Tomasz KamiÅski 
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 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 , 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::operator())
(less::operator(), greater_equal::operator())
(less_equal::operator()): Implement using if constexpr.
(greater::__S_cmp, less::__S_cmp)
(greater_equal::__ptr_comp, less_equal::S_cmp):
Remove.
(greater::__ptr_cmp, less::__ptr_cmp)
(greater_equal::__ptr_comp, less_equal::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 
Signed-off-by: Tomasz KamiÅski 
(cherry picked from commit 8fad43b7850a99b32c48570fc2a3d8ae5a76542a)

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-02-05 Thread tkaminsk at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Tomasz Kamiński  changed:

   What|Removed |Added

   Target Milestone|15.4|14.5

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-02-05 Thread cvs-commit at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #13 from GCC Commits  ---
The releases/gcc-15 branch has been updated by Tomasz Kaminski
:

https://gcc.gnu.org/g:aa01b849ab8589906fbcf82b0d49d4315f8743b8

commit r15-10787-gaa01b849ab8589906fbcf82b0d49d4315f8743b8
Author: Tomasz KamiÅski 
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 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 , 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::operator())
(less::operator(), greater_equal::operator())
(less_equal::operator()): Implement using if constexpr.
(greater::__S_cmp, less::__S_cmp)
(greater_equal::__ptr_comp, less_equal::S_cmp):
Remove.
(greater::__ptr_cmp, less::__ptr_cmp)
(greater_equal::__ptr_comp, less_equal::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 
Signed-off-by: Tomasz KamiÅski 
(cherry picked from commit 8fad43b7850a99b32c48570fc2a3d8ae5a76542a)

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-01-19 Thread tkaminsk at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Tomasz Kamiński  changed:

   What|Removed |Added

   Target Milestone|--- |15.4

--- Comment #12 from Tomasz Kamiński  ---
Will be backported to v15.

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-01-19 Thread cvs-commit at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #11 from GCC Commits  ---
The master branch has been updated by Tomasz Kaminski :

https://gcc.gnu.org/g:8fad43b7850a99b32c48570fc2a3d8ae5a76542a

commit r16-6898-g8fad43b7850a99b32c48570fc2a3d8ae5a76542a
Author: Tomasz KamiÅski 
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 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 , 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::operator())
(less::operator(), greater_equal::operator())
(less_equal::operator()): Implement using if constexpr.
(greater::__S_cmp, less::__S_cmp)
(greater_equal::__ptr_comp, less_equal::S_cmp):
Remove.
(greater::__ptr_cmp, less::__ptr_cmp)
(greater_equal::__ptr_comp, less_equal::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 
Signed-off-by: Tomasz KamiÅski 

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2026-01-16 Thread tkaminsk at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Tomasz Kamiński  changed:

   What|Removed |Added

 CC||tkaminsk at gcc dot gnu.org

--- Comment #10 from Tomasz Kamiński  ---
Similar problem exists for compare_three_way for type that provide
heterogeneous spaceship:
struct Compare
{
  operator int*() const;

  friend std::strong_ordering
  operator<>(Compare, int*);
}; 

Compare c; int i;
c <=> &i; // call C::operator<=>
&i <=> c; // call C::operator<=>
compare_three_way{}(c, &i): // call C::operator<=>
compare_three_way{}(&i, c): // pointer comparision

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2025-12-18 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Jonathan Wakely  changed:

   What|Removed |Added

 CC||rogerio.souza at gmail dot com

--- Comment #9 from Jonathan Wakely  ---
*** Bug 123209 has been marked as a duplicate of this bug. ***

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2025-12-13 Thread Emmanuel.Thome at inria dot fr via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #8 from Emmanuel Thomé  ---
Created attachment 63049
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=63049&action=edit
testcase with std::sort vs std::ranges::sort

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2025-12-13 Thread Emmanuel.Thome at inria dot fr via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #7 from Emmanuel Thomé  ---
I just got hit by this bug (or some further instance of it).

Interestingly, I notice that it creates an unexpected inconsistency in the
behavior of std::sort and std::ranges::sort. I attach another reproducer.

(for std::ranges::less, I believe it happens over here, but this is just a wild
guess
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/ranges_cmp.h#L66
)

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2025-12-13 Thread Emmanuel.Thome at inria dot fr via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Emmanuel Thomé  changed:

   What|Removed |Added

 CC||Emmanuel.Thome at inria dot fr

--- Comment #6 from Emmanuel Thomé  ---
I just got hit by this bug (or some further instance of it).

Interestingly, I notice that it creates an unexpected inconsistency in the
behavior of std::sort and std::ranges::sort. I attach another reproducer.

(for std::ranges::less, I believe it happens over here, but this is just a wild
guess
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/ranges_cmp.h#L66
)

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-06-29 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #5 from Jonathan Wakely  ---
P2825 would be very useful here.

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-06-29 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

Jonathan Wakely  changed:

   What|Removed |Added

 Ever confirmed|0   |1
   Last reconfirmed||2024-06-29
 Status|UNCONFIRMED |NEW

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-02-29 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #4 from Jonathan Wakely  ---
Created attachment 57577
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=57577&action=edit
Check for <=>

Something like this (but it's also needed for std::greater and std::less_equal)

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-02-28 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #3 from Jonathan Wakely  ---
(In reply to Jonathan Wakely from comment #2)
> Without looking at the code, we probably just need to check if the type has
> a usable operator<=>.
> 
> We check it out had


Sorry, phone typos. 

"We check if it has..."

> a usable operator< which worked fine in C++17, but in
> C++20 x

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-02-28 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #2 from Jonathan Wakely  ---
Without looking at the code, we probably just need to check if the type has a
usable operator<=>.

We check it out had a usable operator< which worked fine in C++17, but in C++20
x

[Bug libstdc++/114153] std::less<> prefers operator const void*() over operator<=>() in C++20 mode

2024-02-28 Thread marc.mutz at hotmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114153

--- Comment #1 from Marc Mutz  ---
It's only the C++14 "diamond"/is_transparent version of std::less/greater_equal
that is affected. If you replace the return from main with greater_equal{},
then it calls op<=>, too:

// https://godbolt.org/z/cnjssh3ss
return std::greater_equal{}(arr[0], arr[1]) ? 0 : 1;
//^ added