User model stacking: current status
Brian Goetz
brian.goetz at oracle.com
Wed Jun 15 17:51:06 UTC 2022
> All else being equal, the idea to use "inaccessible value type" over
> "value type doesn't exist" feels very good and simplifying, with the
> main problem that the syntax can't help but be gross.
Yep.
> * It feels wrong to restrict access to the type only because of two
> very specific things we don't want people to do with the type. We
> don't want them to write `new TheType.val[size]`, and we don't want
> them to write `TheType.val someUnintializedField;`. Is there a third?
> And can we really not just prevent those specific things? It feels
> like baby/bathwater, especially since delayed initialization scenarios
> like those are already problematic in many ways as it is.
This is where I started; that the thing really being protected against
is _creating heap locations with their default value_, which is done by
fields and by array creation expressions. (When we get to specializable
generics, there will be a third: using the .val class as a type
parameter, since `Foo<T>` might have uninitialized T-valued fields, so a
`Foo<P.val>` could have the same problem as any other class that
declares a `P.val` field.)
The reason I backed off is that this seemed (a) a hard border to
explain, (b) calling users attention to low-level details like heap vs
stack values, and (c) the value of being able to use P.val
here-but-not-there is not all that strong.
Hard to explain. Understanding this requires understanding a lot of
low-level things that many Java developers have never thought about,
such as heap vs stack, or the fact that, even if a static field has an
initializer, you can still observe its default value with unlucky
timing. Drawing the border to keep the zeroes out will require a lot of
context, focus on details we would rather users not dwell on, and will
invariably be perceived as a "weird" restriction. Whereas "this type is
private, you can't see it" seems pretty normal.
Small incremental benefit. This one requires appealing to the low-level
details, but basically, the benefit of using P.val instead of P.ref in
stack contexts (method parameter and returns, locals) appears to be
pretty small, because of the excellent calling convention optimization
that we get even on (preloaded) L-types of value classes. The jury is
out on "appears", but it is looking that way. So the argument of "don't
sweat the difference between P.ref and P.val except in the heap" seems
reasonable. \
Similarly, if P wants to, it can dispense safely constructed instances
of P.val[], which are covariant to the exported P.ref[].
So this felt like a reasonable "worse is better" move, but I'm open to
new ideas.
> * I still am saddled with the deep feeling that ultimate victory here
> looks like "we don't need a val type, because by capturing the
> nullness bit and tearability info alone we will make /enough/ usage
> patterns always-optimizable, and we can live with the downsides". To
> me the upsides of this simplification are enormous, so if we really
> must reject it, I may need some help understanding why. It's been
> stated that a non-null value type means something slightly different
> from a non-null reference type, but I'm not convinced of this; it's
> just that sometimes you have the technical ability to conjure a
> "default" instance and sometimes you don't, but nullness of the type
> means what it means either way.
Here's the chain of reasoning that works to get to this state of affairs.
- Reference types don't tear. The JMM gives us strong safety
guarantees about references to objects with final fields. We want this
to work the same way for references to value objects as well as
references to identity objects, because otherwise, the "immutability
means thread safety" promise is undermined.
- P.ref is a reference type; P.ref[] is an array of references.
- For non-atomic value classes, P.val fields can tear under race (and
similarly elements of P.val[]).
- If we spelled .val as !, then switching from P[] to P![] not only
prohibits null elements, but changes the layout and _introduces
tearing_. Hiding tearability behind "non-null" is likely to be a
lifetime subscription to Astonishment Digest, since 99.9999 out of 100
Java developers will not be able to say "non-null, oh, that also means I
sacrifice atomicity."
The link you probably want to attack is this last one, where you are
likely to say "well, that's what you opted into when you said
`non-atomic`; you just happen to get atomicity for free with references,
but that's a bonus."
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20220615/3c37f4ab/attachment.htm>
More information about the valhalla-spec-observers
mailing list