Attendees: Remi, Tobi, John, Dan Smith, Frederic, David Simms, Victor Rudometov, Karen
AI: Karen: send out ways in which value types can become null AI: Dan: send out summaries/tradeoffs of nullability strategies corrections/clarifications welcome I. Value Type upcoming plans? Remi would like to see a release without waiting for reified generics. We are all in agreement about phasing: proposing: step 1: EA binaries that support erased generics - need agreement on how to handle nullability with nullable value types - to get feedback/validation on this approach step 2: Preview with language support with erased generics farther future: support for reified generics/generic specialization. II. Nullability - the key open design issue for our next step Assumptions we now all agree on: based in part from user feedback from LW1 - many thanks! 1. To support erased generics we need nullable value types 2. From a JVM perspective we need the default for value types to be the same as for Object, i.e. nullable, i.e. LPoint; in a vm descriptor is nullable if Point is a value type. Tradeoffs from the JVM level: backward compatibility with erased generics: which requires nullable value types JIT optimizations: need null-free value types There are additional language-level tradeoffs such as exposure to syntactic complexity. It is a language design choice what the nullability default is for value types at the source level. Karen clarified that for LW1: javac rejected nulls for any value types, the jvm implementation decoupled value type consistency from the nullability check and has runtime null checks. So we need to move beyond this mismatch. Remi: Is nullability of value types declaration-site or use-site? John: Qualitative difference between local type checking and multiple compilation units. e.g. if nullability can not be constrained locally, it may require a dynamic check. LW1 taught us: 1. fields and array elements: flattenable or not - decision needs to proceed regardless of the static knowledge of callers 2. Analogously, method calls, implementation artifacts such as vtables, need design constraint similar to heap storage, i.e. need to make decisions regardless of static knowledge of callers Generics breaks assumption that Value Types attribute can be used to assume that all value types are non-nullable. Consider concept of Constraints on Descriptors. We don’t want new descriptors with new rules. We want LDescriptors with a rider which only added constraints, no expansions LW1 showed us that runtime checks can cover many value type nullability checks. The ValueTypes attribute does not cover generics or value-based-class migration. Optimizations unlocked for value types (ed. note - that are not null) - flattenable - method calling convention Both are global contracts Need side channel information such as ValueTypes attribute or additional information on descriptor. We need finer-grained nullability information to reduce the requirement on cross-compilation consistency. “find the primitive” exercise: How do we describe a reference to a value type as nullable or non-nullable To reduce friction with generics and erasure - changed idea - need to have the default for new types be nullable. There are also some wormholes in our value type consistency checking, e.g. MethodHandles and we need to reify a global contract. Propose that we reify a first class descriptor - which is more future proof than QTypes vision: Add constraints as riders to explicit types. This is extensible This provides a predictable requirement and can be also used by specialized generics, for language design and migration design. Example for specialized generics: List<ComplexDouble> != List<ComplexFloat> What if we continued to use List as an interface, and used a descriptor to add constraints See Type Operators JEP (ed. note: JDK-8204937 ) We might want to phase this in Frederic is also exploring a proposal to add suffices to type descriptors, e.g. for nullability Karen: Note that a key benefit of using descriptors is the granularity they offer, not at the class file level, that allows for JIT optimization and backward compatibility in the same file Remi: would prefer a side table Karen: need descriptor for field and method declaration and access note: array creation will need enhancing Frederic: anewarray today only has Class_info for the element, so needs extending One approach is to extend a small number of bytecodes to operate either on a Class_info or on a reference John: proposing extending Class_info to support descriptors Remi: type annotation on code e.g. want method attribute for bytecode John: channel null-rejecting/null-accepting in 1) descriptor vs. 2) ValueTypes attribute Descriptor is more localized note: no one wants to use the type annotations approach Descriptor approach: extensible mechanism Type with a tail: constraint only, restricts the value space, not extend Verifier can check value space restrictions, and may allow some restricted->unrestricted conversions Karen: let’s start with explicit conversions with exact matches for the verifier and evolve from there John: generics did that, in future the verifier could handle no-op conversions Remi: how would you express this in the java language? John: this is a vm mechanism, that is a language choice Remi: Would javac track nullability John: A source processor that did not understand type constraints could rely on dynamic checks Remi: concerns about leakage if stored in a local variable John: Var x = myMap.get() // result is unconstrained Dan: tentatively the type system may provide a way to express nullability John: already expresses unconstrained Remi: javac - objects are always nullable John: language level take - want javac to assume restrict nulls for value types unless evidence to the contrary, e.g. calls to erased generics that semantically return null or explicit source decorations - not clear what a local variable will denote - might want inverted defaults for value type nullability at the source level Remi: concern: erased generic returns null -> this could spread checkcast could prevent null from propagating John: Clojure/Scala - have their own descriptor constraints could decouple with indy on checkcast Remi: value type and nullability as separate concerns what about a reference type that is non-nullable from the vm point of view John: nonnullable and value type might be the 1st use. We need an extensible mechanism Remi: why not value type attribute John: could explore value type as a descriptor extension Remi: what if we solved this at the java level, if java wants a nullable VT, use erasure and checkcast? John: Dan is exploring possible models Getting user back pressure - don’t take away nulls Liked the trick of erasing to object lost the battle due to language requirements Remi: if erase to Object/Interfaces, this is not backward compatible Not forward compatible either with expectations of overloading in java language Have overloading today because we don’t have Object as root for primitives and value types What if we got a value from generics already erased and let java handle this? Karen: we need to handle both erased generics and value-based-class migration Remi: Create folks want value types, and want to retrofit existing classes - they don’t want to lose sync and mutability. ed. note: You can only pass-by-value if you have no identity and are immutable. Karen: Descriptor contraints extensibility could provide additional use cases for optimization, such as “frozen” or just identity less. We chose value types as a sweet spot that solves a number of use cases and can be optimized. Additional discussion on overloading John: one goal is to migrate List<valuetype> to a reified generic. We could do dynamic checking and speculating, it is more performant to do reified generics The nullability contract of a container impacts the implementation of the value space Remi: If map.get() were to return a value type, we don’t see the null until we publish or invoke a method John: with the more complex descriptor model, getfield has fewer runtime checks Karen: goals is to have information for javac, verifier and runtime so that javac can have the same contract for nullability and NPEs that we have today for ClassCastExceptions - i.e. if your code compiles without warnings you will not get a runtime CCE should apply to NPEs (or ArrayStoreException) Remi: concern about heap pollution Guava has an unsafe cast to take a public static method()Object and return anything John: we should distinguish between heap pollution and the far riskier other levels of unsafe Remi: what if javac could handle erased generics without descriptors? And require a null check before a null escapes Karen: we would lose the value-based-class migration John: If Remi is describing: if we only had the ValueTypes attribute and one descriptor and the language folks handled any null erased to Object, then we have to check for wormholes, such as MethodHandles or other cases in which the ValueTypes attributes differ. Karen: I don’t see a language solution for value-based-class migration. Note also that there are multiple ways to get null, not just through bytecodes. Remi: What if we had a small jdk-only list of corner cases for migration? Karen: Dan has pointed out and I agree with him that many users are going to want the benefits of migrating to value types, and even in the JDK, there are already experiments beyond the small list of existing VBCs. If we have a way to avoid corner cases that would be a much more maintainable design. Remi: But if sync and == don’t work there is no way to migrate most classes. And if value-based-class properties are not enforced, there is no guarantee they are followed. Karen: recognize that - we will be offering a flag to check identity - one for javac lint and one for runtime. Karen: note also that for erased generics we have two null issues 1. returning null 2. passing array in and writing null goal is a javac warning before a runtime NPE Remi: Can we find a way to solve this at the language level, javac can give a warning but the vm doesn’t need to know Kotlin has 2 types: nullable and nonnullable with compiler enforcement Frederic: clarification Kotlin has references that are nullable or not, not types John: Had been exploring using erasure for the nullable value type case and the ValueType attribute implying null. Dan is exploring possible alternative translation strategies with different approaches to distinguish a non-nullable value type from an old erased value type Remi: concern is that if we solve nullability here for value types, we may miss something for the bigger nullability picture. .net got this wrong and requires too much complexity with little user benefit. Longer-term: want non-nullable Exploring how to solve value types and reified generics without involving the vm. John: would be happy to defer a nullability vm feature until reified generics or after Remi: wants longer-term non-nullability, e.g. for Strings, with vm optimizations .net added too much source level line noise for little benefit Karen: we need to handle nullability for value types for more than just erased generics. We have mentioned value-based-class migration. There are 8-9 different ways in which value types can become null at runtime. AI: Karen - send nullability cases Dan: Will send out a description of current options we are exploring to handle requirement for null-accepting and null-rejecting value types.