Nullable ValueType, Generics and other vexations
forax at univ-mlv.fr
Thu Aug 30 00:02:36 UTC 2018
just to formalize a little more my thinking about the interaction between (nullable) value types and erased generics.
With LW1, the VM enforces that a value type can not be null, so each time there is a conversion between an Object to a value type, a nullcheck is inserted.
This works great until you start to use already existing code that give a meaning to null, like j.u.Map.get is specified to return a V or null,
so a code like this (with Complex a value type)
Map<String,Complex> map = ...
Complex complex = map.get("a-key-that-does-not-exist");
throw a NPE before a user can even check if complex is null or not.
>From Java the language point of view, a solution is to have a way to express that a value type can be nullable, by mandating a users to write
Complex? complex = map.get("a-key-that-does-not-exist");
and teach javac how to do a null analysis (guaranteeing that you can not call a method on a Type? without a test of null first).
The question is how to translate to bytecode something like Complex?.
We have two choices, one is to teach the VM what Complex? is by adding a bit along with field/method descriptor the other is to erase Complex? like we erase T (to Object or an interface).
I believe is that we should choose the latter solution
- reifying the null information for the VM means solving the null problem of Java not only for value types but for every types, because if we come with a partial solution now, it will hamper our design choices if we want to extend it latter. And solving the nullablity problem is not one of the goal of valhalla, valhalla is hard enough, making it twice hard make no sense.
- having nullable value types reified in the VM is not enough to allow the migration between a reference type to a value type, programs will still choke on identity, synchronized, etc. But it helps for value based classes, yes, but it's the tail wagging the dog. There are few value based classes defined in the JDK and we know from the whole module 'experience' that a contract defined in the javadoc and not enforced by javac or the VM means nothing.
- erasure works because either a value type comes from a type parameter so it is already erased or it comes from a new code so it can be erased because if there is a signature clash due to erasure, it's in a new code so the user can figure out a way to workaround that.
Note2: because of separate compilations the '?' information need to be available in a new attribute (or in an old one by extending it) but only the compiler and the reflection will consume it, not the VM.
Note2: we can also restrict further the use of '?' by disallowing to use it in method parameter unless it's either on a type variable or in a method overriding another one that uses T?, but that a language issue.
More information about the valhalla-spec-observers