User model stacking: current status [Observer discussion]

Tim Feuerbach webseiten.designer at googlemail.com
Sat Jun 18 13:12:26 UTC 2022


Hi João,


> Date: Fri, 17 Jun 2022 19:32:15 +0100
> From: Jo?o Mendon?a <jf.mend at gmail.com>
> To: valhalla-spec-observers at openjdk.java.net
> Subject: User model stacking: current status
> Message-ID:
> 	<CADYQ0T7cwiM4_HW-uNj7cxEvx631QsNPqo_wshifVeSd5o+r9w at mail.gmail.com>
> Content-Type: text/plain; charset="utf-8"
>
>> For locals, it's pretty clear we don't have to answer, because locals
>> cannot be accessed unless they are DA at the point of access. But for
>> fields, we have a problem -- and for arrays, a bigger one. We can try to
>> require that fields have initializers, but there are all sorts of
>> situations in which a field can be read before its initializer runs. And
>> arrays are much worse.
>
> I'm sorry, Brian, are you saying that *the compiler* can't enforce
> definite-assignment on non-final fields and arrays when declared with a
> non-nullable no-zero type like String! or Rational!?


By leaving the constructor before definite assignment, you can read an 
uninitialized final field:

class A {
   final String definitelyAssigned;

   A() {
     printAssigned();
     this.definitelyAssigned = "assigned";
   }

   void printAssigned() {
     System.out.println(definitelyAssigned);
   }
}

The problem is not limited to methods of your own class; you can leak 
`this` in the constructor through subclasses or just handing it over to 
someone else. Of course this is bad style, but the compiler does not 
prevent you from doing it.

If I interpret Brian's mail correctly, he is concerned about leaking a 
default value that could actually be illegal from the point of view of 
the value class:
> Quick observation: the default value of `String!` is less dangerous to 
> allow free, because its "null", and the user can already freely make 
> nulls.   Whereas the default value of `Instant` is not something the
> user can get their hands on, if they can't observe fields of type 
> `Instant.val` during initialization.  So the problem may be less 
> severe for B1.

A big part of Java's integrity model 
is that instance creation should not bypass the constructor (that 
doesn't mean you can't, e.g. using serialization or 
sun.misc.Unsafe). When you declare a B3 class, you explicitly allow the 
JVM *not* to call the constructor on initialization. 
This is counter intuitive, but as John explains here, necessary: 
https://mail.openjdk.org/pipermail/valhalla-spec-experts/2021-December/001710.html 


So from my point of view as a layman, I see four options (leaving out 
arrays here):

     1) "!" and "val" are really separate. B2! is a non-nullable 
_reference_ type, and observing the null before its first assignment is 
unfortunate, but as Brian wrote, B2 = null is a value anyone can already 
create. B3! may be a non-nullable reference type as well for consistency 
(it is "untearable").
     2) B2! *is* B2.val and requires initial assignment following the 
rules of definite assignment, but it carries a separate "initialized" 
bit, and you get null polution as in 1) if you observe it before its 
first assignment. The JVM has to check this flag whenever you read the 
field.
     3) Like 2), but instead of an "initialization lock", you live with 
the fact that it is possible to obtain the illegal default, and blame 
the user of your class for exposing it. Thus as a class author, if you 
opt-in to "value", you buy into instances of your class being in an 
illegal state (for a short time). In this world, instead of making the 
"val" public, B3 rather makes its all-zero default public, so you could 
say "Point! p = Point.default", which is optimized away by the compiler 
to just leave the field as is after spraying zeroes over it. I think 
this aligns well with how Rémi asked for a mandatory no-body constructor 
in the thread linked above.
     4) Like 3), but you limit the places where the default can 
escape,by making up strict rules for using "!" on a field. 
For the above example, the rule simply would be "do not expose `this` 
before first assignment to all non-nullable fields".

Unfortunately, it doesn't stop on instance members. As Brian points out:

> even if a static field has an initializer, you can still observe its 
> default value with unlucky timing.

You can leak an unitialized class member in the same vein as above, but 
it's even worse, because this time you have less control over the thing 
that escapes; if there is a publicly accessible static path to the 
field, anyone you call could potentially access it during 
initialization. But you can't forbid calling anything when assigning to 
a static field, otherwise how do you construct the value class instance 
you want to assign?

2)-4) all have the additional downside that they mingle always-atomic 
references and non-atomic value classes behind the same symbol "!", and 
in the future painted by Kevin where "!" would become the default and 
"?" is the exception, you need to consult the docs whether you 
atomically update a non-null reference type, or are in the process of 
tearing a non-null value type. A way to mitigate this could be to force 
the type user to turn off the atomic knob on their side as well. 
Alternatively, you teach developers about the dangers of tearing 
(Kevin's argument) and always use `volatile`, `AtomicReferenceArray`, 
locks etc. for updating state concurrently.

Tim



More information about the valhalla-spec-observers mailing list