On Mon, 19 Jan 2026 at 11:01, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Mon, Jan 19, 2026 at 11:53 AM Jonathan Wakely <[email protected]> wrote:
>>
>> On Mon, 19 Jan 2026 at 08:44, Tomasz Kamiński <[email protected]> wrote:
>> >
>> > 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 hat erogenous calls.
>>
>> I'm not sure what "hat erogenous" was meant to say :-)
>
> "heterogeneous"
>>
>>
>> >
>> > This patch addresses above, by also checking for free-functions and member
>> > overloads of operator<=>, before fall backing to pointer comparison. We do
>>
>> "falling back"
>>
>> > not put any contains on the return type of selected operator, in particular
>>
>> "contains" -> "constraints"
>>
>> > 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.
>>
>> I would have missed checking the reversed args, and the unconventional
>> return types from operator<=>.
>
> I also missed them originally, but decided it would be worthwhile to test 
> mixed operators,
> and they failed for (const char*, CSTr) cases.
>>
>>
>> > 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.
>>
>> A nice solution - thanks.
>>
>> OK for trunk with the commit message fixes mentioned above.
>
> What about backports? It is C++20, but produces hard to debug issues.
> (I was thinking about letting it sit for a week or two and then backporting 
> it).

Yes, I agree with that plan, then backport to gcc-15. We can consider
backporting further if we think users will still care about using
C++20 with gcc-14. I think gcc-13 doesn't matter for C++20 now.

Reply via email to