Type inference in inlined lambdas

#1

Hey everyone. I have the following issue/question/suggestion:
Let’s assume that we have some class with the following function:

class AClass {
    inline fun <reified T> get(): T = TODO()
}

And let’s also assume that we have some extension function on this class with the following function:

inline fun <reified T> AClass.wrapped(crossinline receiver: AClass.() -> T): Wrapper<T> 
        = Wrapper.wrap { receiver() }

Now, my common sense tells me that it is only natural if all of these functions are inlined and with reified generic types, I should be able to do the following:

fun foo() {
    val a = AClass()
    val wrapped: Wrapper<String> = a.wrapped { get() }
}

But apparently, I can’t. Compiler can infer the type that should be returned inside this lambda with receiver, but cannot auto cast the return function, which is also inlined and with reified generic type. So I have to do this:

fun foo() {
    val a = AClass()
    val wrapped: Wrapper<String> = a.wrapped { get<String>() }
}

I need this kind of functionality to add easy-to-use support of wrapping classes for the library I’m currently working on. So I want to understand why compiler behaves in that way. Is this intended behavior? If so, what lies behind the scenes? In not, should we create an issue ticket or is this kind of functionality is already staged for future releases?

Thanks to everyone in advance! I’m very dived into Kotlin, but this is my first discussion here.

#2

Hello. Can you please submit an issue at http://kotl.in/issue with a self-contained code example (notice that Wrapper is not defined). Thanks!

#3

Sure, no problem.
Actually, I solved this issue with a kind of a hack, but will definitely submit an issue. And will post the workaround tomorrow.
Thank you!

1 Like
#4

So, regarding the workaround to anyone who might find it useful.
Instead of declaring the wrapping function’s argument as receiver of AClass in this case, I created another class with the same functions, which is generic:

class BClass<T>(private val a: AClass) {
    inline fun <reified T> get(): T = a.get()
}

And the wrapping function would be:

inline fun <reified T> AClass.wrapped(crossinline receiver: BClass<T>.() -> T): Wrapper<T>
        = Wrapper.wrap { receiver(object : BClass<T>(this) {}) }

As I understand, this forces the compiler to limit the possible return type in the inlined lambda to the outer reified type, thus it’s possible for compiler to infer it.
Although, I consider it some kind of a hack with a bit of overhead.

#5

The issue is reported to the YouTrack: https://youtrack.jetbrains.com/issue/KT-30823