Valhalla EG notes Aug 29, 2018

Karen Kinnear karen.kinnear at
Fri Aug 31 15:24:52 UTC 2018

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:

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

Is nullability of value types declaration-site or use-site?

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

  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
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.



More information about the valhalla-spec-observers mailing list