It seems like ==
can coerce the right argument. Which is mostly relevant for unsizing coercions (but not exclusively). And unfortunately it coerces by value. [E.g. Deref-coercions don't really come into play, because &mut _
and &_
already have so many different PartialEq
implementations, so that (at least typically), coercions are removed by the compiler before they could ever materialize, in order to avoid ambiguous types.]
I think any fix to the situation would at a minimum have to break code like this:
use std::cell::Cell;
trait Trait {}
impl PartialEq for dyn Trait + '_ {
fn eq(&self, _: &dyn Trait) -> bool {
true
}
}
fn f<'a, 'b: 'a>(x: Cell<&'a (dyn Trait + 'a)>, y: Cell<&'b (dyn Trait + 'b)>) {
x == y;
}
because even if we added a case to "avoid the coercion if the type isn't changed", such a check would realistically not be able to consider lifetimes.
Additionally, it would of course affect some drop orders, though realistically, those were super surprising anyways. (It drops the right argument early, but not the left).
Here's a case with slices which doesn't work with a PartialEq::eq(…)
-desugaring either
fn f(x: Box<[i32]>, y: Box<[i32; 3]>) {
x == y; // works (but consumes `y` by value)
}
(nice that it works, but also kind-of confusing, especially regarding the drop-order)
Here's a case with reference to pointer coercion:
fn f(x: *const u8, y: &mut u8) {
x == y;
// PartialEq::eq(&x, &y); doesn't work
}
though it's less bad because this still doesn't consume y
(it produces &raw const *y
in the MIR).
IMHO, long-term it could be reasonable to fully replace ==
with the PartialEq::eq(&left, &right)
desugaring that the reference promises, at least over an edition, assuming the broken cases can probably all be fixed by rewriting left == right
into left == (right as _)
, which is easily machine-applicable.