There's been a lot of change since the last update of this JEP, so let me summarize the changes and the motivation.  The current JEP text is still very rough; the point of sharing it in this state is to sketch out the next chunk of the story and how it connects to the other chunks, even if they are not fully fleshed out.

#### Value and "primitive" classes

This JEP positions the Valhalla story as one of "the more flexibility you give up, the better flattening you get." Identity classes get no flattening.  Value classes (B2) give up identity (but nothing else), and they get better flattening on the stack (e.g., optimized calling convention), but (absent VM heroics) no flattening on the heap.  This is already confusing to many users, because they hear "flattening" and think "layout inlining."

To get heap flattening / layout inlining, you have to give up some more.  It took a while to figure this out, but we finally realized that the key distinction between B2 and B3 has to do with its _initialization protocol_ (or lack thereof). Non-nullability is important for flattening, but it is secondary; before you can go non-nullable safely, you have to obviate the need for null in the first place, which means you need to make initialization optional.

We have nulls because most objects require initialization; simply interpreting a block of zeroed memory as an instance of a class is usually only incrementally worse than interpreting a block of garbage as an instance of that class.  To protect from interpreting uninitialized memory as an object instance, object references use null as their default value.  The primitive types all share the characteristic that the zero value is actually a good default, and therefore they do not need the extra initialization safety afforded by object references and null, and therefore they can be non-nullable, and therefore they can be routinely flattened.

With this in mind, we downplay the distinction between B2 and B3; B3 is a special kind of value class, one for which construction is optional because the default (zero) value is considered good.

#### Performance story

The performance story becomes one of a spectrum of tradeoffs:

 - Identity classes get lots of flexibility, but no flattening.

 - Value classes give up identity, and get flattening on the stack (optimized calling convention, reliable scalarization, etc), but no flattening on the heap.

 - Value classes that support default instances (B3) give up on the requirement that all instances be initialized, and in return, non-nullable variables can be flattened in the heap -- up to the limit of atomic load/store operations.

 - B3 classes that further opt out of atomicity can get heap flattening for larger instances.

#### Ref, val, and bang

The previous version of the story divided uses of B3 classes into a primitive type and a reference type, and there were a number of properties that derived from the choice of reference vs primitive (including nullability).  We've now arrived at the point where we can represent these properties individually, and therefore don't need to appeal to this coarser ref-vs-val distinction.  As a result, we can get rid of the clunky X.ref / X.val notation, and express null-restriction with the more familiar cardinality sigil, `X!`.

For purposes of this JEP, the use of null-restriction types is limited to types where the null-restriction can be reified by the JVM (hello, Q types); a separate JEP will explore extending it to all reference types and the attendant consequences of erasure (e.g., null pollution.)

In this interpretation, we do not have to call `Point!` a primitive type; we can call it a restriction of the reference type `Point`.  (If we do, we gain some uniformity for class types ("everything is a reference"), at the expense of permanently living with a more visible seam with primitives; we can continue to evaluate the tradeoffs here.)



On 3/21/2023 12:10 PM, Dan Smith wrote:
I've updated JEP 401, formerly "Primitive Classes", now titled "Null-Restricted 
Value Object Storage". The purpose of this JEP is to allow for flattening of value objects in 
fields and arrays. This update gets there via two key features:

- "Optional" constructors, which express the capability of a value class to 
have instances created outside the normal construction process.

- Null-restricted types, which exclude null from the type's value set, and in 
the case of value classes with optional constructors, allow for a non-null 
default value.

There's more to say about nullness, which is covered by its own JEP that we're 
still working on. But the idea of null-restricted variables is enough to 
unblock progress on value object flattening. (Eventually, we envision—at least 
for now—delivering the two JEPs at the same time.)

The document is here:

https://openjdk.org/jeps/401

Reply via email to