Home > Back-end >  Kotlin context receivers cannot resolve members of generic type
Kotlin context receivers cannot resolve members of generic type

Time:11-02

In the following code, the call member of Animal cannot be resolved even though Cat is specified as context receiver and it has a member named call.

interface Animal { val call: String }
object Cat : Animal { override val call: String = "Meow" }
object Dog : Animal { override val call: String = "Woof" }

fun <T : Animal> acquireAnimal(animal: T, block: context(T) () -> Unit) {
  block(animal)
}

fun main() {
  acquireAnimal(Cat) {
    call
  }
}

When I type this inside the lambda, then the IDE seems to suggest that the type of this is Any?.

example where "this" with generic context receiver resolves to Any?

If I do the same with a function without a generic context receiver, then it seems to get the type right.

example where "this" with non-generic context receiver resolves to the correct type

Is this a limitation that is by design or is this a bug?

CodePudding user response:

The fact that you cannot access call was a bug, which was fixed in Kotlin 1.7.20.

A workaround for lower versions is:

sealed interface TypeWrapper<out A> {
    object IMPL: TypeWrapper<Nothing>
}

fun <T: Animal> acquireAnimal(animal: T, block: context(T) (TypeWrapper<T>) -> Unit) {
    block(animal, TypeWrapper.IMPL)
}

fun main() {
    acquireAnimal(Cat) {
        val x = call // works!
    }
}

However, the fact that this doesn't work is intended. Context receivers do not change the meaning of this. Since you are in a global function, this does not mean anything, and the existence of a context receiver does not change that.

Normally, to access the context receiver itself, you need to do a qualified this by appending the generated label for the context receiver:

context(Foo)
fun foo() {
    val x = this@Foo
}

However, your context receiver is a type parameter, so according to the rules here, I don't think a label is generated for the context receiver.

  • Related