Hello! I've declared function `inline fun<reifi...
# compiler
p
Hello! I've declared function
inline fun<reified T> test() {throw AssertionError("Implementing by compiler plugin")}
In IR generator I want to replace all calls to this function by some expression (using T type literal). For calls with direct literals (for example
test<Int>()
) it works as expected. But when calling from templated function with template parameter it appears to substitute just type parameter. For example
inline fun<reified T> test2(){test<T>()}
Is it possible in IR generation phase to transform all actual inlined types?
m
Afaik this is not possible. The actual inlining happens after the IR phase. Hopefully this will change in the future. I had a similar issue and came around with a slightly different solution. It looks like this:
Copy code
Public api stub
inline fun <@CustomReified T> test(): KClass<T> = error("Stub")

// user code
fun test2() {
    val stringClass = test<String>()
}

// transformed code
fun test2() {
    // type is known -> transform in place
    val stringClass = String::class
}  

// user code
inline fun <@CustomReified T> test2() {
    val tClass = test<T>()
}

// transformed code
inline fun <@CustomReified T> test2(provideTypeT: () -> KClass<T>) {
    // type is unknown -> let the caller pass the result
    val tClass = provideTypeT()
}

// user code
fun test3() {
    test2<String>()
}

// transformed code
fun test3() {
    // pass the known type
    test2<String> { String::class }    
}
Instead of
reified
I added a custom annotation for these kind of type parameters. Then everytime the special function get's called I check if the type is fully known If yes -> do the transformation If not -> add a lambda parameter to the function which propagates the responsibility to the caller. Feel free to ask more if you have any questions.
s
You probably can just add a synthetic parameter with the type of
KClass?
instead of lambda, I think, but general approach that Manuel suggested is probably the best. You can then substitute type parameters with the known type at the outer call and propagate it to the places where it is unknown otherwise.
m
@shikasd I used a lambda because it might be expected to return a new instance for each call of the stub function depending on the use case. The lambda would get inlined in the end because it's an inline function.
p
@Manuel Wrage Thank you!
Hopefully this will change in the future.
where did you get hope?
m
@PHondogo From my perspective it would make sense to share as much code as possible between the compilation targets(JVM, JS, Native). But I don't know if there are any technical issues which prevent this.
The only concern I have is that it's not clear how inline function calls should be represented in the IR. A solution could be something like a
IrInlineFunctionCall
expression which contains information about the inline function call + the inlined function body.
s
For now, inlining happens much later in the lowering stage than the extension is called (extension is effectively the first). So theoretically, compiler team can provide a api to hook into later levels of compilation, but for now, you can even inline functions yourself. However, on jvm, inlining happens on the bytecode level, and only info about top declaration is kept across modules. So here you cannot really change how it was called and you have to rely on hacks like those anyways.