On Mon, Jan 19, 2026 at 12:54 PM Jonathan Wakely <[email protected]> wrote:
> 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. > I have just backported the patch to GCC-15. I will also backport is to GCC-14, as we already use if constexpr as extension in this versions, and operator<=> was one of the first features I started using with C++20.
