I tried to help someone on a discord server who posted a snippet that didn’t compile, but I wasn’t able to find an answer to why it wouldn’t compile. I managed to generalize it to a minimal runnable (or, in this case, not runnable) example:
object A
object B
fun A.foo() = println("Test A")
fun B.foo() = println("Test B")
fun main() {
var test: A? = A
test = null
test?.foo()
}
(playground link: Kotlin Playground: Edit, Run, Share Kotlin Code Online)
the way I expected it to work is that test
is of type A?
, so no matter what happens, .foo()
called on test
would refer to the A.foo()
overload. But since test
is set to null
before the safe call, the call simply shouldn’t execute and nothing will be printed.
Instead, the snippet doesn’t compile with the error: “Overload resolution ambiguity: public fun A.foo(): Unit defined in root package in file File.kt public fun B.foo(): Unit defined in root package in file File.kt”. So basically, the compiler cannot decide whether test?.foo()
refers to A.foo()
or to B.foo()
. Which makes no sense since the type of test
is explicitly declared as A?
and there’s no mention of the B
type whatsoever in the main()
function.
But I think the weirdest part is what happens when you remove the test = null
line:
// same code as before goes here
fun main() {
var test: A? = A
test?.foo()
}
(playground link: Kotlin Playground: Edit, Run, Share Kotlin Code Online)
Now, the code will compile just fine, and then obviously print Test A
. The same happens when you change the initial value of test
to null
(playground link: Kotlin Playground: Edit, Run, Share Kotlin Code Online), it’ll then just not run the safe call and output nothing. Same goes for the val
variants of those.
It seems like there’s some weird type inference happening when null
is later assigned to the var
, but that makes no sense since the type of test
is explicitly declared, so type inference shouldn’t have a say in this. The fact that adding just an assignment statement makes the compiler fail on a different statement is what makes it seem like a compiler bug to me.
But I don’t know, is there someone who has more insight on this topic and could explain this to me?
I tested this with all available Kotlin versions on the playground, and also with JS Legacy and JS IR, all of them showed the same behaviour.