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

John Rose john.r.rose at oracle.com
Mon Feb 12 19:12:59 UTC 2018


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.

> 
>>> (10) withfield, Linking exceptions:
>>> 
>>> "The field must be final, it must be declared in the current value class, and the
>>> instruction must occur in a method of the current value class. Otherwise,
>>> an IllegalAccessError is thrown."
>>> 
>>> Per point made above, javac would emit withfield only from (a) static value factory method(s) and not any method of the current value class.
>>> 
>> Thinking more about this, it might be time to drop __ValueFactory and allow
>> all methods from a value class to use withfield. I cannot remember the argument
>> in favor of stricter rules for this bytecode.
> 
> Problem in allowing all methods to use withfield is that it will make the final keyword meaningless as it is defined now.

That's not right.  Final means "you cannot perform putfield" (plus JMM effects).
It says nothing about withfield.  This is not just hair-splitting:  withfield produces
a *new* instance of the value.  Final is only about preventing side effects to an
existing (object) instance.

> It is one thing to say a specially privileged method that is really a factory and so works with nascent values is allowed to update instance fields that are marked final and that it really results in copy-on-write semantics. There is precedence for such - a constructor is privileged to set blank final fields. (indeed it must), quite another to open the floodgates.

There are no floodgates here; there is a legitimate need for withfield to support field
updater methods directly, instead of indirectly by reclassifying them as constructors,
or refactoring them to call hidden constructors.

> I am not saying it cannot be done, but we need to redefine finality for fields of a value type in order to be able to do that.

See above.  It's not even a redefinition, just a refusal to carry the existing definition
(no putfield outside of constructor) to a new place where it doesn't belong.

> In that case, should we even require them to be final ?

Yes, that's the issue.  There's no strong reason other than (partial) consistency
to require fields to be final on value types.  The physics of values requires that
putfield must never work regardless of field access flags, because putfield requires
an object operand, not a value operand.

> Why won't we simply state that all updates to instance field would result in a copy ?


That's probably already stated.  In any case, it is inherent to value physics.

I think it will be best if we *do* require ACC_FINAL on value fields, for now.

My reason for this is a little tricky:  If we eventually remove the requirement,
one possible meaning (out of several) is to allow withfield to be applied
outside of the value class's nest, just as non-final public fields can be set
from outside an object class's nest.  There may be a different useful
meaning for non-final value fields, in the future, also.  So let's hold
that option open by requiring ACC_FINAL today.

(See also the notion of "sealed field" in objects, which could allow
an object to internally putfield a public field, without allowing
random parties to putfield the field, even though it is public.
This kind of sealing is logically consistent, but probably not very
useful unless we also find a way, such as thread confinement,
to control the JMM effects.)

Bottom line:  Allow vwithfield as a private operation to any method
in the same nest.  Require ACC_FINAL on fields.  Maybe relax
some of these limits in the future.

— John


More information about the valhalla-dev mailing list