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

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) from accepting nulls:

List 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