Could the idea of null-default inline classes be revisited?
John Rose
john.r.rose at oracle.com
Wed May 6 19:46:26 UTC 2020
Our current model is much simpler and more manageable
than the one outlined in [1] and handles most use cases of null.
But, as you point out, it does allow V.default and null to
co-exist as points in the type of V’s box type, just as (int)0
coexists with null in the type Integer. This is just one of
the simplifications that comes from admitting that there
are two (named) types associated with an inline class V,
the inline type itself V.val and its box/reference type V.ref.
(To review: V.val translates to an inline class class file,
while V.ref translates to a non-inline abstract class file.
The two types are sealed together as nest-mates. The
JVM gives them distinct names. The owner of the
“good name” V is up for grabs in the translation strategy.
The result is that V.val : V.ref :: int : Integer. We have
dropped the “clever” technique of deriving both types
from one classfile, because it buys us some stuff. There
are many more details not worth going into here.)
You are suggesting that the current model falls short
in cases where the author of an inline type V really,
really doesn’t want null (in V.ref) to be distinct from
V.default (in both V.val and V.ref). Obviously, the
only place where the distinction shows up is not in
V.val but V.ref. That suggests that you’d like for the
boxing operation (from V.val to V.ref) to include an
optional step to map (V.val)V.default to (V.ref)null,
instead of a non-null reference which boxes the
bit-pattern of V.default.
Any decision like this has deep and far-reaching
consequences. One such consequence here is that,
even if some software module assumes that null is
disjoint from any V instance, the author of V can
subvert that assumption by converting one of the
V.val points to null (when boxed to V.ref). Perhaps
this is a decision the author can make safely and
locally, within the bounds of V’s encapsulation,
but it is hard to predict the long-term effects of
“hiding” the default value of V.
Another consequence is that the JVM has to very
carefully ensure that no instance of (V.val)V.default
ever escapes into the larger type space of Object or
V.ref. This has a cost and a probable bug tail.
Another consequence is that unboxing null to V.val
would have to detect the null and substitute a value
(V.default, the “vull” value for V). That might seem
straightforward, but it’s not; we’d have to define all
the places where NPE would happen on such an
attempted unboxing, and where silent substitution
of V.default would happen. That’s not only complicated,
but likely to lead to a long chain of surprises for
end users. They would go both ways: “Hey, what’s
the NPE in my program? I know I constructed that
value correctly!” Or, “Hey, why did my program
silently accept a method call on that uninitialized
pointer? It took me days to track down that bug!”
Not all the machinery of [1] would have to be activated
to implement your request, because some features of
the two-type model would allow us to do better book
keeping. For example, V.ref.default would be null,
while V.val.default would be a real value, not a real
null. But there is still plenty of complexity in any
of the known solutions to this request.
While I recognize that box-deafult-to-null would be
helpful for some use cases, it’s not just a simple add-on.
I’m inclined to leave it as a possible future extension,
to be opted into by future inline classes, if the feature
gets implemented.
For now, the choice is pretty straightforward: If you
need null as a value in a variable of type V, use V.ref.
If you need flatness in the variable, use V.val. Choose
one.
— John
On May 6, 2020, at 10:29 AM, Mateusz Romanowski <romanowski.mateusz at gmail.com> wrote:
>
> Hi,
> The idea of null-default value types was raised years ago [1], however, I
> believe it was dropped.
>
> Considering that one of the reasons for Project Valhalla was, I believe,
> not to have to choose between using safe abstractions (validating
> constructor) and flat memory layout, replacing unsafe fields (ie.
> firstName:Ljava/lang/String;, lastName:Ljava/lang/String;) with safe inline
> value (ie. person:Qpkg/Person;) that wrap, should be easy without
> surprising average developer.
>
> Unfortunately, as Person.default cannot model real person, the developer
> needs to learn new idiom for *some* inline types (comparing to default
> value instead of null check) and learn that instanceof/cast no longer
> rejects all impossible values.
>
> Also, if generalizing the idea of migration of primitive type and its
> wrapper class [2] into value and reference projection, we might consider
> void/Void migration.
>
> If void becomes alias for value projection java.lang.void and
> java.lang.Void is its reference projection, then Void value set consists of
> void.default and null. This would be a paradox, unless void.default becomes
> null.
>
>
> I think that those might be reasons for introducing L-envelope-using [3]
> null-default inline types/classes.
>
> Are there any chance for having vulls?
>
> Thanks,
> Mateusz Romanowski
>
> [1] https://cr.openjdk.java.net/~jrose/values/nullable-values.html
> [2] http://cr.openjdk.java.net/~briangoetz/valhalla/sov/02-object-model.html
> [3]
> https://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2020-April/001288.html
More information about the valhalla-dev
mailing list