Having worked through the JLS changes (which I'll be sharing at some point 
soon), here are a few extra details that I think make the most sense:

> On Dec 12, 2023, at 4:27 PM, Dan Smith <[email protected]> wrote:
> 
> To enable and take advantage of early field initialization, we've envisioned 
> the following changes:
> 
> 1) As an exception to the general rule about 'this' usage, a 
> "pre-construction context" allows writes to blank instance fields of the 
> class. (The terminology may need updating, since you're clearly 
> "constructing" the object if you're writing to its fields.) The fields are 
> "write-only" at this stage—you can write into them but can't read them back.

Terminology: maybe "early construction context" rather than "pre-construction 
context"?

Enabling the capability to write to *blank* instance fields, but not fields 
with instance initializers, sidesteps any confusion about the timing of 
initializer execution, while still giving programmers the capability to prevent 
any unwanted reads of a field's default (null/zero) value, whether the field is 
final or not. (Would it be nice if fields with initializers could also, in some 
cases, be initialized early? Yes, but it's an incompatible change, so more work 
needed to navigate that problem.)

> At a 'this()' call, all final fields must be DU (because the delegated 
> constructor will perform its own writes). No such restriction is needed for 
> non-final fields; but it's an open question whether we should prohibit all 
> writes before 'this()' anyway.

In the interest of not making arbitrary language rules, I prefer not to 
special-case the early construction context of a 'this'-calling constructor, 
other than introducing the DU rule for final fields.

> Writes to non-final fields with initializers are disallowed, to avoid 
> confusion about sequencing (the field initializer will always run later, 
> overwriting whatever you put in the constructor prologue.)

Yeah, this was the main concern about assignments before 'this()' calls. If you 
can't write to fields with initializers, then the timing of writes to a mutable 
field should be clear from the code. Whether it's a good way to structure a 
program is a stylistic choice. <shrug>

---

And some comments about other proposed features:

> 2) If a final field is written before 'super()' via every constructor in the 
> class, it can be considered a "strict final" field. It will never be observed 
> to mutate.
> 
> In the class file, ACC_STRICT is repurposed to indicate a strict final field. 
> javac is responsible for identifying strict final fields. Existing 
> early-initialized capture fields can probably be automatically counted as 
> strict finals.
> 
> ACC_STRICT implies ACC_FINAL and !ACC_STATIC. Verification ensures that a 
> 'putfield' for an ACC_STRICT field of the current class never occurs after 
> the 'super()' call. (Specifically, the receiver type for the putfield must be 
> 'uninitializedThis', not a class type.)

Although there are limits to how much we can do with the flag just yet (see 
below), I think it probably makes sense to identify these fields in class files 
as ACC_STRICT as part of this JEP.

> 3) Immutability of strict finals is a strong guarantee.

This jumps the gun.

ACC_STRICT is a claim about local code: that the field will not be mutated by 
the code of the class after the 'super()' call.

A global claim about immutability relies on other integrity properties of the 
JDK as a whole. There's a path to getting those integrity properties, but it's 
beyond the scope of this JEP.

> JVM internals may treat strict final fields as truly immutable, without 
> supporting any deopt paths when unexpected mutation occurs.

This will be true only after we can make a global claim about immutability 
(ACC_STRICT, so not mutated by the class itself; plus all off-label mutation 
paths have been blocked or disavowed.)

> The 'Field.setAccessible' method, which provides a standard API mechanism for 
> mutating final fields, considers strict finals to be "non-modifiable", and 
> will not enable reflective writes. (It already does the same for record 
> fields.)

This felt too ad hoc.

A better path is to follow in the footsteps of "Prepare to Restrict the Use of 
JNI" (https://openjdk.org/jeps/8307341), gradually limiting the use of 
'setAccessible' for final field mutation (ACC_STRICT or not) to users who 
explicitly opt in. That would be a separate effort.

> Standard deserialization ensures strict finals are set, and so their values 
> deserialized, before the object under construction is leaked to any user 
> code. This probably means back references to an object from its own strict 
> final fields are unsupported, and deserialize to 'null'. (Records already 
> behave in this way.)

I think this would be nice to have, paired with ACC_STRICT, but we'll see 
whether we can get there or not. If not, it's an improvement that can come 
later.

> Unsafe and JNI are capable of performing arbitrary, type-unsafe modifications 
> to field storage. Clients who modify strict finals do so at their own risk, 
> and JVM optimizations won't try to account for such usage.

I noted the attempts to restrict use of JNI above; but in any case, I think 
this statement is still fair: JVM internals do not need to account for misuse 
of Unsafe/JNI.

> - Can javac check for me that my fields are strict?

I heard a proposed rule that if any constructor early-initializes a final 
field, the field should automatically be ACC_STRICT and an error should occur 
if a different constructor fails to early initialize the field.

I think that gets the new capability backward: it's not that we want to give 
people a new kind of field. Instead, we want the code in constructor prologues 
to have the flexibility to write to fields of 'this', because a broad 
prohibition on all uses of 'this' (including field writes, type variable use, 
enclosing instance access) is too blunt a restriction. *Secondarily*, we can 
notice that certain field initializations follow a pattern that represents a 
useful property, and we can document that in class files.

It's reasonable to want a feature that, as a matter of good style, checks 
fields for early initialization. But that capability isn't the core of the 
feature, and because there are various ways to get there, we're leaving it for 
Phase 2.

Reply via email to