[LW10] Value equality

Brian Goetz brian.goetz at oracle.com
Wed Oct 17 21:03:15 UTC 2018


If we expect values to “work like an int”, then |==| comparisons on 
value types should work like they do on primitives:

|value class Point { ... } ... client code ... /* mutable */ Point 
location; void setLocation(Point p) { if (p != location) { 
fireExpensivePropertyChangeEvent(location, p); this.location = p; } } |

Users will reasonably expect this idiom to work, just as they would for 
|int| properties.

There have been at least four suggested treatments of |val==|:

  * Illegal; compilation error
  * Always false; user must follow up with |.equals()| test
  * Substitutibility test — are these the same value
  * Delegate to |.equals()| (call this “deep substitutibility”)

I think the first is unreasonable and should be discarded, based on the 
above illustration of failure to “work like an int”; I think the second 
is the same.

We expect generic code to use the LIFE idiom:

|(x == y) || x.equals(y) |

The LIFE idiom arose because |ref==| is fast, and so we can optimize 
away the potentially expensive |.equals()| call when called with 
identical objects. But, if the |val==| test becomes more expensive, then 
what started out as an optimization, might lead to duplication of 
nontrivial work, because the |.equals()| implementation may well also 
have an |==| test. But, we don’t want to let the optimization tail wag 
the dog here.

Separately, we have been reluctant to push value substitutibility into 
|acmp|, for fear of perturbing the performance model there. So, let’s 
posit a |vcmp|, a sibling to |{ildf}cmp|, and translate |val==| to that, 
and let |acmp| continue to return false when either operand is a value.

Now, for pure value code, like the above, we translate |val==| to 
|vcmp|, and we work like an int. And for generic code, the LIFE idiom:

|x == y || x.equals(y) |

(when |x| and |y| are of type |T|) translates to an |acmp| backed up by 
an |invokevirtual|, and when |T| is specialized to a value, the |acmp| 
is fast and always false, and when specialized to a reference, is fast 
and computes |ref==|. I believe this gives everyone what they want:

  * value equality is intuitive and appropriately fast
  * Existing generics continue using LIFE, and get fast (enough)
    translation both for reference and value instantiations
  * Generic code that forgets |.equals()| is equally wrong for reference
    and value instantiations
  * Generic code that goes straight to |.equals()| works correctly for
    reference and value instantiation

​


More information about the valhalla-spec-observers mailing list