Revisiting default values

Brian Goetz brian.goetz at
Tue Jul 28 19:06:16 UTC 2020

> I think it would help if we had a clear sense as to what proportion of 
> inline-types we think will have this "bad default" problem. Last year 
> when we discussed null-default inline types the thinking was that 
> about 75% of the motivation for null-defaults was migrating VBC, 20% 
> for security, 5% for "I want null in my value set.". My assumption is 
> that the vast majority of inline-types will not be migrated types, 
> they will be new types. If this is correct then it would appear that 
> the default value problem is really a problem for a minority of 
> inline-types. 

Indeed, we've come up with good solutions for migrating VBCs (migrate it 
to a ref-default inline class) and "I want null in my value set" (then 
just use the ref projection.)

For the "migrate from VBC" crowd, we offer the advice: "keep using `Foo` 
(really `Foo.ref`) in your APIs, but feel free to use `Foo.val` inside 
your implementation, where you are confident of no nulls."  And further, 
we offer that advice to both the VBC author and its clients.  So, we can 
expect existing APIs to continue to return Optional<T>, but more fields 
of type `Optional.ref<T>`, to get the flattening, and doing null checks 
in the constructor: = requireNonNull(foo)

And this is one of the sources of "zero pollution"; a client may have a 
field of type `Foo.val` and just not initialize it in their constructor, 
and then later someone calls ``.  Unlike with a reference type, 
which would NPE in this situation, we might enter the `bar()` method, 
which might not be defensively coded to check for the (meaningless) 
default, and it will do something dumb.  Where dumb ranges from "Welcome 
to 1970" to "delete all my files."

I think what we need for Bucket 3 (which I think we agree is more 
important than Bucket 2) is to (optionally, only for NGD inline classes) 
restore parity with reference types by ensuring that the receiver of a 
method invocation is never seen to be the default value.  (We already do 
this for reference types; we NPE before the dispatch would succeed.)   
And the strategies we've been kicking around have ranged from "try to 
prevent the default from showing up in the heap" to "detect when the 
default shows at various times."

If the important point in time is method dispatch, then we can probably 
simplify to:

  - Let some classes mark themselves as NGD (no good default)
  - At the point of invocation of an NGD instance method, check the 
receiver against the default, throw NPE if it is
  - Optionally, try to optimize this check by identifying (manually or 
automatically) a pivot field

Note that even an unoptimized check is probably pretty fast already: 
"are all the bits zero."  But we can probably often optimize down to a 
single-word comparison to zero.

Note too that we can implement this check in either generated bytecode 
or in the VM; the semantics are the same, the latter is more secure.

More information about the valhalla-spec-observers mailing list