Saw this coming :)

On 6/1/2023 6:26 PM, Kevin Bourrillion wrote:
There is one thing that I simply can never let go without comment (which Brian had to have known :-)).


On Wed, May 31, 2023 at 11:37 AM Brian Goetz <[email protected]> wrote:

    ### Initialization

    The key distinction between today's primitives and objects has to
    do with
    _initialization requirements_.   Primitives are designed to be _used
    uninitialized_; if we declare a field `int count`, it is reliably
    initialized to
    zero by the JVM before any code can access it.  This initial value
    is a
    perfectly good default, and it is not a bug to read or even
    increment this field
    before it has been explicitly assigned a value by the program,
    because it has
    _already_ been initialized to a known good value by the JVM.  The
    zero value
    pre-written by the JVM is not just a safety net; it is actually
    part of the
    programming model that primitives start out life with "good
    enough" defaults.
    This is part of what it means to be a primitive type.


Uninitialized values of primitive types do still cause bugs.

Another way to think about this is the historical progression.

In hardware / assembly language, when you load a value from a register or memory location, and you haven't provably put a good value there, you get an out-of-thin-air value.  There is no "safe initialization" of anything; there is just memory and registers.

C more or less propagates this approach; if you don't initialize a variable, you get what you get.  C doesn't force indirections on you; you have to ask for them.

Java 1.0 moved the ball forward for direct storage, saying that the initial value of any heap variable is zero, and requires that locals be initialized before use.  It also moves the ball forward by putting indirections in for you where they are semantically needed. But Java copies what C did for primitives; in the desire to not make arithmetic ungodly expensive, an int is just a direct machine int, like in C, with a frosting of bzero.

Valhalla gives us a choice: we can flatten more non-nullable things, or we can cheapen (somewhat) the things that use null as a initialization guard.  The good thing is that we can choose which we want as the situation warrants; the bad thing is we can make bad choices.

People will surely make bad choices to get the flattening benefits of B3, because, performance!  But this is not all that different from the other bad performance-overrotations that people make in Java every day, other than this one is new and so people will initially fall into it more.

As far as I know, Valhalla could *maybe* still decide to require that non-nullable variables of value-class types be initialized explicitly. Maybe it would complicate migration too much, I'm not sure. I know it would require one new feature:

`new ValClass[100] (int i -> something())`

For fields, we can close the gap by doing null checks at constructor end (in the same place we emit memory barriers to support the final field initialization safety guarantees.)  The latter is voided when `this` escapes construction, and the former would be as well, but this seems a pragmatic choice which narrows the initially-null problem quite a bit for fields.  As you point out, arrays are harder, and it requires something much like what you suggest (which is also a useful feature in its own right.)  Note that none of this is needed for B3!, only for B1!/B2!.

... which, importantly, must compile to the optimal code (that never actually loops) when `something()` is literally just a call to ValClass's implicit constructor (or is otherwise the default value for the type in question). In case `(i -> MyImplicitlyConstructableType())` is the behavior you want, you'd be a bit steamed to have to write it out. But just *how* valuable is getting to skip it? I wonder.

Nothing wrong with `new B3![n]`, any more than `new int[n]`.  It's B1/B2 that have the problem.

Reply via email to