Field initialization before 'super'
Remi Forax
forax at univ-mlv.fr
Wed Dec 13 16:01:18 UTC 2023
----- Original Message -----
> From: "daniel smith" <daniel.smith at oracle.com>
> To: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> 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
More information about the amber-spec-observers
mailing list