Should `Pointer(T)#==` always compare the address, regardless of `T`?
I'm wondering if Pointer#== should always compare the address, regardless of the pointer type. They're partially identical because they point to the same start address. The exact memory size may differ between types, though. And interpretation differs anyway. 🤔
But it's not about the objects themselves, it's about pointers to them. And pointers are primarily defined by their address.
Originally posted by @straight-shoota in https://github.com/crystal-lang/crystal/issues/16412#issuecomment-3574665363
Note: a pointer is basically an integer after all.
The funny thing is that comparison is generally not strict on types (#10277). But Pointer#==, which is a potentially unsafe type, is very strict about the type...
An alternative could be to disallow equality checking between different pointer types. That would avoid bugs like #16412 as well.
Disallow equality checking between different pointer types
That sounds quite good 👍
It's a bit more complicated when considering different, but compatible types. The situation is already a bit more lax, but only partial:
class Parent; end
class Child < Parent; end
x = Child.new
pointerof(x) == pointerof(x).as(Parent*) # => false
pointerof(x).as(Parent*) == pointerof(x) # => true
Pointer(T) includes Comparable(self), so it defines a #==(other : self) method; since this self is equivalent to Pointer(T), and since this T is covariant in a type restriction, the second line will match Comparable(Parent*)#==. In contrast, the first line will not match Comparable(Child*)#==, and thus falls back to Value#==. Including Comparable(T*) would have the same issue.
This is probably why other generic types do it like Comparable(Array) or Comparable(Tuple) instead, to avoid subtle reflexivity issues like this. Pointer is the only standard library type that uses Comparable(self).
Yes, the implementation of relaxing the type requirement would be to change Comparable(self) to Comparable(Pointer), and #<=>(other : self) to #<=>(other : Pointer).
That would also affect the other method from Comparable. But I think that's fine. If equality only on the address is acceptable, then comparing pointers by their address should be good as well.