Excellent notes as always. (Impressive how you collect them and also take a full part in the conversation.)
On Mar 9, 2018, at 2:13 PM, Karen Kinnear <[email protected]> wrote: > > Attendees: Remi, John, Dan H, Lois, Tobi, Frederic, Karen > > Corrections always welcome. > > John: this is a JVMS, not a java language specification. Java can choose its > own default as well as any language syntax later. Footnote: our current intention, at the language level, is to sweep as much of the null corner casing under the rug as possible. One specific is that I hope the "ACC_FLATTENABLE" bit will be placed on *all* fields that are seen at compile-time to be value types. The two alternatives I hope to avoid are (a) making ACC_FLATTENABLE explicit at the source level requiring explicit opt-in to a flat container, and (b) default ACC_FLATTENABLE with an opt-out that says "make this nullable". One reason: I don't want nullable value types to be supported as a first-class language construct; they would be misused. Another reason: Conversations like "A: this gives me no performance benefit, you are such losers; B: did you remember to set your nullability correctly? A: what's nullability?" And, under the "field == element" heuristic, similar set of considerations for whether to supply value arrays that are only flattenable, only nullable, or sometimes both (and how to set the default in that case). Desired positions are (a) flat is the default, and (b) nullable may be available as a workaround if you try hard, but not as a first-class language feature. > Frederic: rationale is the compatibility story. Allows existing > value-based-classes like Optional to migrate to become value types and allow > classfiles that declare Optional fields, and clients that access those fields > to continue to work unchanged, including the ability to store/retrieve a null. > > Note: arguments, local variables can contain a null for a value type, it is > only when publishing that an NPE is thrown. > > John: not calling this non-nullable from a language feature perspective. > Might be something more like “probable flattening”. > > Remi: why preload? > John: If we defer loading we might get circularity errors which should > prevent the container from loading Preloading, of field types, is required in order to discover the layout of a field type. This isn't necessary for object types since those are always reached by pointers, and all pointers are laid out the same. The need for preloading a field type is only necessary if the field type is a value type *and* the containing object is going to (attempt to) flatten the field. The discussion I recall is that Remi wasn't asking why we preload, but rather why *not* preload more often, in order to more correctly decide in favor of flattening. In particular, we preload superclasses (again for layout reasons!) and interfaces (for layout reasons, in the v-tables), so why not just preload every field type, on the chance that it *might* be value type, and then we can DTRT. I wish we could! IIRC Karen went through this exercise and noticed that various circular loading dependencies made this impossible. Loading all of your supers is possible because the subtyping graph is acyclic, so you can use a lazy loading algorithm to get everything straight. (With lots of hairy infra to manage concurrency and cycle detection. See ClassCircularityError.) Doing this for fields doesn't just require *more* hairy infra, it is *impossible* because field incorporation is a cyclic graph, even for very simple programs. Perhaps I misunderstood Remi's question. Remi has a very clever proposal for the compiler to emit an extra attribute in the classfile which says "I assume the following types are value types". This would allow limited preloading, and we could exclude cycles in this graph. (The reason is that you can't have value A including value B directly or indirectly; you logically need an object somewhere in the graph, and that breaks any cycle in Remi's attribute relations.) The use of the modifier bit ACC_FLATTENABLE, on fields only rather than general value types, is a simplification and limitation of Remi's clever proposal, which we hope will be sufficient as well as easier to specify and implement. > ... > Remi: what about compatibility? > if you read an array that is empty today, you get null > if you read a flattened array, you get a default value > > John: with arrays we do not need to do any preloading so no benefits to not > always flattenable > believe limited use cases for non-flattenable arrays (Remi's clever proposal of a "values committee" in every classfile, which determines the potential flatness of fields, may lead to annoying questions like, "does the committee also determine the flatness of arrays created by that class?" and "does the committee also determine the null-permissiveness of instruction xyz?" As well as dealing with cases when the committees in different classfiles make different decisions.) > solving the JVM part - not the user model or language > > Remi: concern Optional[] - will no longer see nulls > John: Optional is not supposed to be seen in the heap > - with generics, there are no Optional[] that are reified - so erasure > saves us > Remi: Kotlin, ??, Scala - reify > John: languages can make their own compatibility decisions > Remi: can buy argument > John: let’s prototype this and see One option for other languages, that I think will be workable in many cases, is to use erasure to implement the occasional case of a nullable use of a value type. I.e., erase the value type to its bound, and insert casts. (Make user null checks happen before any cast, since the cast will probably NPE on nulls.) So erase the null-permissive type `ComplexDouble?` to JVM Object and surround with casts of `ComplexDouble!` as needed, where the JVM type of `ComplexDouble!` is `ComplexDouble` Another more complex option is to wrap. Where erasure goes upward, wrapping goes outward. Implement `ComplexDouble?` with an auxiliary value type with a single nullable field (lacking the ACC_FLATTENABLE bit). Or make an object type which boxes. > ... > John: language decision - maybe use C++ notation, drop the “new”, e.g. > (typename)arg , … "Drop the new" is TypeName(arg), as in var i = ComplexDouble(0, 1); That's the consensus. Despite the fact that it's ambiguous, since there might be a method nearby called ComplexDouble. Since this is unlikely we can add a rule which resolves the ambiguity in a compatible way (take the method if there is one) and be done. In such cases you also need a way to opt into the non-legacy behavior. That would involve an explicit qualifier on the class name, including maybe something new like ComplexDouble.ComplexDouble(0, 1), just to handle the vanishingly unlikely second-order corner cases on the corner cases. (Doug Lea's guidance was, "just do it; don't worry about the corner cases".) > ... > Dan: Why would vm offer a canonical name? > > John: <init> today has special rules > <make> - no special rules for verifier > - naming - ensure not in valid namespace for java programmer > > Dan H: new/dup/<init> in nest in future? John: yes > John: yes. <init> has signature with void return today, maybe if use <init> > with non-void return? > Remi: if retrofit void as a value type -> then not distinguishable > John: maybe <make> overloaded with args. must always be static (I'm going to send a separate note about this.) — John
