No, it wouldn’t. It would infer T = Int (which is <: NonNull), and then Int <: Int | Null so you can pass 0 as a valid argument to an Int | Null.
That’s the thing, there aren’t really good examples of functions, or type signatures, that should reject null. In general, there aren’t good examples to reject specific types.
Functions want to accept certain inputs, because they want to perform certain operations on them (like call a given method). Rejecting specific types does not give the function any new capability. Rejecting means a) less freedom for the user b) without any new capability/freedom for the implementation. That is a lose-lose situation for everyone, so this is virtually never what we should do.
The only use case that people bring up that requires rejecting specific values is to use those values (typically null) as sentinels. I have argued at Make Null a subclass of AnyVal under -Yexplicit-nulls - #56 by sjrd that this is a misuse, and that there are better alternatives that don’t require rejecting specific values.
It’s unfortunate that JSpecify defaults Foo<T> to mean T extends @NonNull Object (technically Object NO_CHANGE). As I argued previously, IMO there is almost never a good reason to restrict null out of a general type parameter, so they’re choosing the IMO-wrong interpretation by default.
At the same time, because it’s probably the wrong thing to do anyway, I also think it’s not a big deal if we don’t represent it exactly. It’s probably fine to translate a JSpecify Object NO_CHANGE to our <FromJavaObject>. The latter allows any S <: <FromJavaObject>, including if Null <: S. Yes, it means Scala would allow instantiating a type parameter to a nullable type, although JSpecify said it was not allowed, oh well
.
I don’t think adding NonNull will carry its weight. It would only be useful for Java interop cases, and then only for Object.
For other classes, Scala interprets them as non-nullable anyway. For Object mentioned in Scala code, it is non-nullable as well. It’s only a Java interop issue because we translate Object mentioned in Java code to <FromJavaObject>.
Perhaps a path forward for JSpecify-annotated code would be to introduce <FromJavaObjectNonNull>. Then, like we have the rule:
S <: <FromJavaObject>ifS <: Any
we could potentially add something like
S <: <FromJavaObjectNonNull>ifS <: Anyand notNull <: S.
That would contain the “NonNull problem” to Java interop.
With that path forward, there’s no real need to solve the problem now. We can decide to add that later if experience actually shows that it is really an issue. I expect that interpreting Object NO_CHANGE as our existing <FromJavaObject> will result in a better experience than trying to shoehorn NonNull in there.