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