Normally, the value produced by a getfield instruction cannot be more sharply typed than the type guaranteed by the JVM, i.e., type denoted by the descriptor of the field.
In some cases, however, the action of an object's constructor can modeled in such a way that that type of the value stored in a field can be sharpened. For example, a reference field can be proven non-null (on immediate exit from a constructor) if the constructor provably stores a non-null value to the field, either by building a non-null value or calling Objects.requireNonNull. A more subtle example is that an array field value might always be initialized to an array of some definite length.
This RFE proposes that we revive some prototyping work done years ago by C2 engineers to model the type-flow effects of constructors on final fields, and use the resulting information to sharpen the types of getfield values loaded from such fields.
This work does not apply to mutable fields, since a constructor does not have the final say on the value of a mutable field. It might be extended to mutable fields, if we extended the analysis to all putfield instructions everywhere in a class, but great care would be required to ensure that reflective updates to fields either would not violate the sharpened types, or that code compiled under the assumption of sharpened types could be deoptimized after a violating reflective store.
Even final fields (before Valhalla) can be reflectively modified (although this is rare outside deserialization frameworks), so similar reflective guards are required for both final and non-final fields.
But this work is more profitable for Valhalla than for the standard JDK. The final field in an inline type is truly immutable, so we can expect that reflective updates (and/or updates via Unsafe mechanisms) will be rarer and (perhaps) easier to exclude or monitor.
Also, this work is easier to accomplish with record types, since (a) their fields are properly immutable, and (b) all object construction goes through a single canonical constructor.
(Earlier prototyping work found that in a class with multiple constructors, there was some benefit to tracking field type-state through each constructor independently, and then keeping track, for each value of a given class, which constructors might have been used to create that value. The resulting getfield types are the union/join of of getfield types producible by any constructor which might have built the instance in question, for a given getfield. This analysis becomes simpler when all construction goes through a single canonical constructor, as with records.)
The type sharpening can apply to fields of any completely-constructed object. It would have to be disabled for incomplete objects. This means (a) during the optimization of a getfield in an object whose constructor is or might be on the stack, or (b) at any point, if an object's constructor unsafely publishes the object. Excluding (b) makes it easier to enforce (a), with an observation that, if a constructor call is on the stack, it is in the current JIT compilation unit (given certain rule enforcements).
Suggestion: The first time a class is reflectively updated, make a crude type-flow pass through its constructor(s) and derive sharper types (if possible) for the fields. If this produces a win, compile a guard function to dynamically re-validate an instance's sharpened field values (e.g., null checks and array length verification, per examples above). Install this guard function and run it after every reflective update; it's not needed for regular code. Make an API for validation for use by Unsafe APIs, as needed. A failed validation will un-install any code that relies on the sharpened types, and also removes the validation function; falling back to the weakened types.