Is this a bug for kotlin smart cast

code:

class B {
    operator fun invoke() {
        println("class B")
    }
}

class A(val b: B?) {
    fun methodA() {
        if (b != null) {
            b.invoke()
            b()
        }
    }
}

fun functionA(b: B?) {
    if (b != null) {
        b.invoke()
        b()
    }
}

fun b() {
    println("function b")
}

fun main() {
    functionA(B())
    A(B()).methodA()
}

why the result is:

class B
class B
class B
function b
1 Like

This behavior is correct according to this stack overflow answer:

Thank you answer, It’s my problem.
My focus is not about distribution, but about smart cast.
If we delete function b in this case, we well get a compile error in methodA:
Error:(27, 13) Kotlin: Reference has a nullable type 'B?', use explicit '?.invoke()' to make a function-like call instead
But this error won’t be in functionA.
Actually my question is why does smart cast only work in functionA? Is it because b is a class properties in methodA?

functionA acts normal: b cannot change and it knows it.

methodA accesses a field. Globally speaking, fields can change.

val t : Double get() = rand().takeIf{ it > 0.5 } 

In this case, however, the field cannot change as it has no custom getter.

It seems almost all functions are aware of this:

  • you can call normal functions on b;
  • you can access b by their operator overloading (tried get and unaryMinus);

The only one (I’m aware of) which does not about this is invoke: you can call it as a function, but you can’t invoke b.
This seems indeed like a bug.

class B {
    operator fun invoke() = Unit
    operator fun get(name : String) = 5
    fun test() = Unit
}

class A(val b: B?) {
    fun methodA() {
        if (b != null) {
            //allowed
            b.invoke()
            b.test()
            b["I"]
           //not allowed
            b()
        }
    }
}

This looks like https://youtrack.jetbrains.com/issue/KT-4113, please vote

1 Like