Revisiting default values
Brian Goetz
brian.goetz at oracle.com
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:
this.foo = 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 `foo.bar()`. 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