Nullness markers to enable flattening

Brian Goetz brian.goetz at oracle.com
Wed Feb 8 15:15:51 UTC 2023



On 2/8/2023 9:27 AM, Remi Forax wrote:
>> The goal is a general-purpose feature that
>> lets programmers express intent about nulls, and that is preserved at runtime
>> sufficiently for JVMs to observe that "not null" + "value class" + "non-atomic
>> (or compact) class" --> "maximally flattenable storage". There are no "value
>> types", and there is no direct control over flattenability.
> I would say that "not null" + "zero default value class" is necessary for flattening.
> Then "non-atomic class" can help, but there is no guarantee.

Layout decisions are the purview of the JVM; Valhalla's approach to 
flattening is to allow the user to influence flattening by selecting 
semantic properties that remove impediments to flattening.  An 
approximate rubric for "when will things flatten" (in the heap) is:

     type is identity-free
     && variable is null-free
     && (type layout is small || type is non-atomic and not huge)

There are other heroics the VM can do to get more flattening (e.g., 
using slack bits to represent nulls in certain types), but this is the 
basic idea.  And these are all either _semantic_ things the user 
controls (identity-freedom, nullity, atomicity (which equates to the 
lack of cross-field invariants)) or things that it is entirely 
reasonable for the VM to make layout decisions based on (e.g., don't 
flatten huge things).

The main challenge of Valhalla has been teasing apart the various 
concerns to get them down to fine-grained properties we're willing to 
ask users to incorporate into their programming.

>> - Nullness is generally enforced at run time, via cooperation between javac and
>> JVMs. Methods with null-free parameters can be expected to throw if a null is
>> passed in. Null-free storage should reject writes of nulls. (Details to be
>> worked out, but as a starting point, imagine 'Q'-typed storage for all types.
>> Writes reject nulls. Reads before any writes produce a default value, or if
>> none exists, throw.)
> The last sentence worry me a little, we have types that does not have a default value ??

"Sensible defaults" has been a long-standing challenge.  The VM has a 
very, very strong bias towards the default state of memory being all 
zeroes.  It is convenient that this yields sensible defaults for our 
primitives (numeric zero) and references (null pointers). Nullable types 
have a good zero default: null.  "Well behaved" value types like Complex 
have a good zero default: zero.  But types like LocalDate are 
problematic: the zero maps to a sensible value, but its a terrible 
default.  So types like LocalDate, while they could admit some 
flattening (and get good flattening on the stack), need protection in 
the heap from uninitialized values, and so LocalDate wants to be nullable.

That is: if your zero default is bad, you need null as a default. This 
is one of the central reasons for having Bucket 2.  (Spoiler: I don't 
like bucket 2.)




More information about the valhalla-spec-observers mailing list