--- Comment #28 from Jonathan Wakely <redi at gcc dot gnu.org> ---
I can commit a patch that fixes the original testcase, but I now think that the
resolution of https://wg21.link/lwg2450 requires compiler help of some kind, at
least for cases like comment 27. It effectively says "if x < y uses the
built-in operator< for pointers, that comparison yields a total order". That's
very different to "if x and y are pointers, comparing them yields a total
order". The library can't tell whether x < y uses a built-in operator for
pointers, because there could be conversions involved, so the library can't
decide whether the cast to uintptr_t is needed.
(In reply to Jakub Jelinek from comment #21)
> So, any progress here on either the standard side or GCC side? Do you still
> need a builtin for constexpr compatible reinterpret cast from the FE, or can
> it be solved solely in the library?
Even such a magic cast doesn't help, because the library can't tell when that
cast should be used. We can't unconditionally cast everything to uintptr_t,
only pointers, but we can't tell which operands convert to pointers.
One solution would be a new __builtin_less(op1, op2) builtin that does all the
- if overload resolution for op1 < op2 results in comparing two pointers, then
return the result of (uintptr_t)op1 < (uintptr_t)op2 instead, in a
constexpr-compatible way (including rejecting comparisons that aren't allowed
in constant expressions);
- otherwise, just evaluate op1 < op2 as normal, which may or may not be
constexpr-compatible, depending on decltype(op1) and decltype(op2).
Then std::less would just use __builtin_less(x, y) and everything is done by
the compiler. And similarly for __builtin_greater/greater_equal/less_equal.
Another solution might be __builtin_totally_ordered(op) which results in some
tree type that wraps op, and when used as the operand of a relational operator
it performs overload resolution as would happen for op, and if that would
compare pointers then op is converted to (uintptr_t)op and that is compared
instead. Then std::less would use:
return __builtin_totally_ordered(x) < __builtin_totally_ordered(y);
But this seems a lot more work to implement, with no advantages.
Another solution would be to change the standard, so that std::less<void> only
requires a total order when the arguments are pointers, and not for arbitrary
types that might convert to pointers.