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.


Reply via email to