Compiler option(s) proposal: provide nullability assumptions when interacting with non-annotated Java code

#1

The documentation is currently clear that null-safety guarantees begin to deteriorate when interacting with non-Kotlin code. I’m not looking to propose a change to default compiler behavior, as I imagine this topic was thoroughly explored and discussed long before my (admittedly recent) adoption of the Kotlin language.

That said, I was hoping to discuss a potential opt-in compiler feature that could enforce more guarded interactions with non-annotated Java code. Consider the following minimal example:

fun uuidFromString(val uuid: String?) = UUID.fromString(uuid)

The UUID.fromString() implementation from java.util is currently non-annotated, and will throw at runtime if it receives a null value. And as we’re bridging Kotlin and Java domains, the Kotlin compiler will not warn or fail when I attempt to invoke this method with a value of the nullable type String?.

I’ve searched around for some rationale on why this was the preferred default compiler behavior, but haven’t seen much beyond a comment suggesting that the alternative was too cumbersome. At any rate, I accept that this is unlikely to change as a default behavior. BUT, is there room to explore compiler options that would enforce more guarded code when bridging the language barrier?

Specifically, as pertains to the code example above, I would love to see a compiler option that rejects any attempt to pass a nullable value to a Java parameter that hasn’t been explicitly annotated as nullable as well. This would also rely on corresponding annotations (on the Kotlin side) that explicitly permit an unguarded invocation in situations where this was deemed safe.

Thanks in advance for any consideration!

1 Like

#2

So you’re proposing a compiler option to treat all platform types as nullable types?

0 Likes

#3

No (although I suppose that would be categorically in the vein of what I’m discussing). But I’m actually proposing compiler options that force more guards around null safety during interop, not less.

As a point of initial discussion, but I’m proposing a compiler option that would treat parameters in a Java function as explicitly non-nullable (barring the existence of any annotations that indicate to the contrary). This would prevent a caller in a Kotlin context from unintentionally invoking the function with a null value in a potentially unsafe way.

0 Likes

#4

I see.

So you want to require non-nullable arguments wherever platform types are currently required:

val x: Int = 0
someJavaMethod(arg1) // OK

val x: Int? = null
someJavaMethod(arg2) // Error

I misunderstood and thought you were also talking about returned values:

val x: Int? = someJavaMethod() // OK
val x: Int = someJavaMethod() // Error
0 Likes

#5

That’s correct, I was attempting to describe what you show in your first example.

That being said, your second example would also be a candidate for a related compiler option. The general spirit fo these proposed compiler options would be to force null-safe handling during Java interop in cases where nullability is unknown.

0 Likes

#6

You could achieve some this using inspections–although for passing args from Kotlin to Java, you may have to write your own. I think you can set IntelliJ to treat certain warnings as errors.

But still, with the proposal, how would I call a Java method that accepts null but isn’t annotated?

javaMethodThatExpectsNull(null) // Error
0 Likes

#7

Per my initial proposal, I was imagining that this would require annotations on the caller side:

This would also rely on corresponding annotations (on the Kotlin side) that explicitly permit an unguarded invocation in situations where this was deemed safe.

E.g.,

@AllowUnknownNullability
javaMethodThatExpectsNull(null)

Alternatively, the compiler options could incorporate some sort of an -allowunknownnullability java.util.* pragma (or something to that effect).

1 Like

#8

Before Kotlin was released, they automatically annotated standard library with nullability annotations and there were no platform types. But they decided to move on from this model. I suppose, inferring nullability from bytecode at compilation is very slow process, maintaining external nullability annotations for each library is burdensome and error-prone (imagine typescript-like situation where you need “annotation” addition for each library and its version) and treating every value as nullable will cause very bad code (I don’t know about you, but I can’t stand numerous !! everywhere or mindless checks).

That said, at this point I believe it should be an optional mode. May be someone will come with automatic nullability detector to generate external annotations. From that we will need community to maintain those annotation libraries. It’ll allow to remove most of platform types.

0 Likes