value type hygiene
paul.sandoz at oracle.com
Thu May 10 16:47:54 UTC 2018
> On May 10, 2018, at 8:52 AM, Brian Goetz <brian.goetz at Oracle.COM> wrote:
> Thanks for this great writeup. I find much to agree with here, and a few things to be concerned about (I’ll express the latter in a separate mail; Dan touched on some of them.)
> Now that we see it, elevating from ACC_FLATTENABLE to the ValueTypes attribute makes obvious sense. The key thing to reify is whether V was a value type at the time C was compiled. This flows into many decisions within C, and at the boundary of C and other V-users, so capturing it in one place makes sense.
> I’ll add that this reminds me very much of loader constraints. When class C calls method D.m(P)R, we first textually match the call with m(P)R in D via descriptor match, and then we additionally make sure that C and D agree on any loader constraints, throwing an error if they do not. In L-world, whether C and D think V is a value or object class is another kind of constraint. At linkage time, if these constraints agree, they can use an optimized protocol; if they disagree, rather than failing, the VM can introduce hidden adaptation to iron out the disagreement.
Also bridges generated by generics are naturally a place for such checks/adaptions from the ref world to the value world, the cast could be coopted to perform the null check and throw e.g. forEach'ing with a Consumer<Value> over a List<Value>.
> This is a big win over the use of bridges in Q-world, since the adaptors are only generated at runtime when they are strictly needed, and as the ecosystem gets recompiled over time to a more uniform view of V’s value-ness, will steadily go away. We saw shades of this in Albert’s first prototype of heisenboxes, where the JIT compiled multiple versions of each method (if needed) according to different views of value-ness, and then fit them together, lego-style.
> A note on the responses:
> - I think the Map.get() discussion is a red herring. This is a signature that simply makes no sense when V is a value. We’ve looked at several alternatives — optional-bearing, a pattern match (case withMapping(var v)), a get-with-default, etc. In Q-world, we observed that sometimes a method doesn’t make it to the any-fied version; it becomes a restricted method that only makes sense on reference types. In L-world, we don’t necessarily have “ref V” to fall back on (though we might), but there will need to be some way to give Map.get() a gold watch and thank it for its service (and lament that the best name has been retired from the namespace.)
Yes Map.get has to somehow retire (although i still think it represents a good use case of what to do at the boundary of the value and ref worlds, perhaps List.get is a better case to discuss in this regard) IMO that’s part of the hygiene we need to do to the libraries. I just don’t have a strong sense on how to retire this if value types and specialized generics proceed at different rates. We can start by deprecating it (with forRemoval? that might be tricky), but perhaps it requires some compiler and linkage error when used with values?
> I’ll start a separate thread on my concerns.
More information about the valhalla-spec-observers