Equality for values -- new analysis, same conclusion

Brian Goetz brian.goetz at oracle.com
Mon Aug 12 21:02:38 UTC 2019

>> Where this world runs into more trouble is with specialized generics; we’d like to treat specialized Foo<T> as being generic in “T extends Object”, which subsumes values.  This complicates things like bound computation and type inference, and also makes invoking Object methods trickier, since we have to do some sort of reasoning by parts (which we did in M3, but didn’t like it.)
> Yikes, I don’t understand these particular issues fully, but they sound scary.

If we have a class

     class Foo<T> {
         T t;

         boolean equals(Object o) -> (o instanceof Foo oo) && o.t == oo.t;

Today, this is an erased class, so when we instantiate Foo<V.Box>, we 
get Object== semantics for the last comparison.  But tomorrow, when we 
make this a specialized class, Foo<V> will get specialized with val== 
for the latter comparison, since `this.t` will be statically typed to 
`V` rather than `Object`.  If we assign anything other than SAME== to 
Object==, then upon specialization, Foo<V.Box> and Foo<V> will have 
different equality semantics.  (Remi will say that the bug here is using 
==; this is neither wrong nor right, but simply evidence that there is 
more than one way to attack this problem.)

> Part of the job of splitting the difference between int-nature and Object-nature involves deciding what to do with box/unbox conversions (which are part of int-nature).

Our plan, ideally, is to demote the boxes, in favor of a lighter-weight 
boxing conversion to a value box.  Then, the object-primitive divide 
effectively goes away, and we are left with everything is an object.  
(Perhaps it is a mistake to reuse the term "box" as it is laden with the 
old associations of "accidental identity" and "slow".)

> The “gravity” of the situation prompts us to click into a tight orbit around either “boxes and unboxes like an int” or “subtypes Object like a class”.  Maybe there’s a halfway (Lagrange) point out there somewhere, such as “unboxes like an int and subtypes Object like a class”, where unboxing artifacts show up (as a special form of cast) but not boxing artifacts.

In fact, that's where we currently are with the eclair story.  V 
converts to V.Box via subtyping, but V.Box converts to V (when erased 
values hit sharply-typed client code) via unboxing.

> Looking very closely at the current cycle of int-box-Integer-unbox-int, maybe we can limit the irregularities (the schism mentioned above) in some way that allows us to “blame” them on the unbox step.  After all, the “codes like a class” part only implies one direction of type conversion, from some V to Object or V.Box (the eclair interface).   Going the other way, from V.Box to V, is a pure “works like an int” move, since Java does not allow implicit down-casting, only unboxing conversions.  And we can view V.Box-to-V as an unbox *as well as* (or maybe *instead of*) a down-cast.

That pretty much describes the erased-generics-via-eclairs story.

> This suggests a new category of syntactic context for operator==, V.Box.  So we have val==, val.box==, and Object==.

With the same caveat as above: if Val.Box== is not the same as Val==, on 
specialization, we risk observing semantic changes.  And if it's 
subtyping, not boxing, I think its a forced move.

> So I’ll repeat that SAME== saves us a lot of trouble, since it is already compatible with prim== and Object== (except for float== and double==).  If we can use SAME== (except for double== and float==) we win.
And you find yourself with me in the bottom of the gravity well. Hi, 

More information about the valhalla-spec-observers mailing list