----- Original Message -----
> From: "daniel smith" <[email protected]>
> To: "amber-spec-experts" <[email protected]>
> Sent: Wednesday, December 13, 2023 1:27:24 AM
> Subject: Field initialization before 'super'

> In Valhalla we've been building on the language changes in JEP 447 (Statements
> Before Super) to move towards a more safe and reliable programming pattern for
> initializing final fields. Some of these ideas could make their way into the
> next iteration of Statements Before Super, to be further augmented with Value
> Classes (JEP 401).
> 
> Two key observations:
> 
> - Inside <init> methods, the JVM allows writes to instance fields of
> "uninitialized" objects, before the 'super()' call. (In fact, javac has long
> used this capability to initialize fields that store captured state of inner
> classes.)
> 
> - When a 'final' field is written before the 'super()' call, it is impossible 
> to
> observe the field prior to its initialization. Thus, the field can be treated
> as truly immutable—every 'getfield' on the same instance will return the same
> value. (In contrast, in existing usage, uses of final fields may observe
> mutation if the object might still be under construction.)
> 
> 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.
> 
> The regular DA/DU rules apply for final fields: they must be initialized 
> exactly
> once by an initializer or by every 'super()'-calling constructor, whether in
> the prologue or the epilogue.
> 
> 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.

I would say, keep that restriction.
Having code before this(...) or even after this(...) is usually when you start 
to have fields that are written several times making the initialization steps 
hard to follow.

> 
> 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.)

This seems to suggest that final fields with initializer should be written 
before the call to super() by default even if it's not a backward compatible 
change.

> 
> 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.

I would be more "strict", if a final field is written before super() in one of 
the constructor, it should be written before super() in the other constructors.

> 
> 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.)

Before reusing STRICT, do we need a release were using STRICT is considered as 
an error, because currently, STRICT is just ignored ?

> 
> 3) Immutability of strict finals is a strong guarantee. JVM internals may 
> treat
> strict final fields as truly immutable, without supporting any deopt paths 
> when
> unexpected mutation occurs.
> 
> 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.)

yes,
and all synthetic fields (from capture) should be strict, because currently 
captured fields in lambda are like strict but captured fields is inner classes 
are like normal fields.

> 
> 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.)
> 
> 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.
> 
> -----
> 
> That covers "phase 1" for this feature. Eventually, we'll want to address
> questions like
> - What about fields with initializers?

If it's final, move them before super() and pray that it's a too incompatible 
with existing codes (at least we should try). 

> - Can I have my implicit 'super()' call go at the end of my constructor?

I would like to avoid to allow non final fields to be initialized before 
super() so no.

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

That's the hope.
For me the real issue is with fields with initializers, if it's too 
incompatible, we will have to introduce a new keyword "strict" for them, I hope 
we do not have to.

> 
> These sorts of capabilities probably make sense to introduce with value 
> classes,
> and perhaps retrofit on records. Further design work needed to figure out how
> to release them for general consumption. All of that can be considered "phase
> 2", to come later.

+1 for retrofitting records and lambda proxies.

> 
> But for Statements Before Super, we're just proposing to start with (1), (2),
> and (3).
> 
> I realize (2) and especially (3) are stretching the original concept of this 
> JEP
> (which was purely language/compiler-oriented). But I think, from end users'
> perspective, it will all feel like the same feature. If wanted, though, I 
> could
> see doing those pieces in their own JEP in parallel with Statements Before
> Super.

regards,
Rémi

Reply via email to