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?
.
If I do the same with a function without a generic context receiver, then it seems to get the type right.
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.