Moving from VVT to the L-world value types (LWVT)

John Rose john.r.rose at oracle.com
Wed Feb 21 02:48:59 UTC 2018


On Feb 12, 2018, at 11:12 AM, John Rose <john.r.rose at oracle.com> wrote:
> 
> On Feb 12, 2018, at 8:53 AM, Srikanth <srikanth.adayapalam at oracle.com> wrote:
>> 
>> Hi Frederic,
>> 
>> A couple of follow up questions below:
>> 
>> On Monday 12 February 2018 10:02 PM, Frederic Parain wrote:
>> 
>> [...]
>>> The current design allows null references for value types, as long as they are not stored
>>> in a container (field or array) declared as flattenable. This is a significant change from
>>> previous design. So, casting null to a value class type is now legal.
>> 
>> OK.  This does not call for any change to the specification of checkcast in JVMS ?
>> (I don't know that it does - Just double checking)
> 
> There might be an option here:  Maybe we could get away with having checkcast
> throw NPE if the target class is a value type.  After all, the checkcast instruction resolves
> its class operand.  Remember that we *must* allow polluting nulls in fields which
> are not explicitly marked as flat, but *only* because of binary compatibility requirements.
> We *do not* allow polluting nulls to be stored into arrays, because arrays *must*
> resolve their element types before the first array is constructed.  I think we could
> put checkcast into either of these two categories:  Allowing polluting nulls for
> compatibility, or forbidding them (throwing NPE).
> 
> Hmm…  The more aggressive choice (throwing NPE on checkcast) would make would
> prevent instances of generic code (such as List<DoubleComplex>) from accepting
> nulls:
> 
>  List<DoubleComplex> xs = new ArrayList<>();
>  xs.add(null);  // to NPE or not to NPE?
> 
> This is not really a JVM question, but a language question:  Should
> the above code throw NPE if freshly recompiled?  Yes, probably.
> What if it is inside a legacy classfile, and is not freshly recompiled?
> It would break if it threw NPE.  This argues for two slightly different
> versions of checkcast.  Argh:  An option turns into a two-sided mandate.
> The checkcast might need a flat-bit.  (Stay away, you Q-types.)
> Or the checkcast could throw NPE only in newer classfile versions.
> 
> These considerations also apply to Class::cast.

I thought about this some more, and talked briefly with Brian, and
here's where I think we should come out:

In new source code it will always be illegal to cast a null to a value type. 
The rule for values is, "codes like a class, works like an int". If you cast 
a null to (int) you get an NPE. That's what the language needs to do. 
It doesn't matter what the JVM does. 

There are two exceptions to this reasoning: Explicit nullability, or 
legacy compilation. If we were to *add* a new feature which allowed 
a type to be nullable, then types which had this nullable aspect 
could be the target of a cast, without a null check, and the JVM 
would process the null appropriately. It would have to use an 
L-type descriptor for the value, of course. But we are not doing 
this at present. 

If javac is compiling for legacy code (say, with the experimental 
features turned off) then it should not "notice" class files which 
are defined as value types (or non-nullable types, if we add this 
feature at some point). In that case, *no* null checks should be 
inserted, since that would be incompatible with old code. 

To summarize: javac should arrange a null check when it emits a cast 
to a type which is statically known to be a value type, if it is compiling 
at a source level which recognizes value types as such. 

Since reflective calls are used by both old and new bytecodes, 
adding a null checks for value-types is not an option for Class::cast. 
Probably we need a new API point, Class::castValue, which senses 
a value type and adds a null check in that case. For reference types, 
the behavior of Class::castValue would be the same as Class::cast. 

At the JVM level, we should consider making checkcast incorporate 
a null check, in the case where (a) the class resolves to a value type, 
and (b) the classfile version supports value types. In this way, 
legacy classfile code will continue to be sloppy with nulls, but new 
code will not. Users who need to cast to a value type *without* 
the null check can use Class::cast or some other API point which 
guarantees to allow "polluting" nulls to pass through.

To be clear:  If a classfile contains a checkcast that refers to a
value type, *and* if the classfile's version number (or other markings)
allows it to process value types (say, withfield is also allowed),
*then* the semanatics of the checkcast *should* include a null
check, even though (for compatibility reasons in old code) the
JVM would allow a null under the same descriptor.

I admit that giving checkcast a different behavior in different
classfile versions is odd, and as a temporary fallback it might
be better to ask javac to insert explicit null checks before
such checkcasts.




More information about the valhalla-dev mailing list