Let's check the following code:
// lib fun process(x: Int) { /* some code here */ } inline fun run(block: () -> Int) = process(block()) // main fun foo() { run { 42 } }
We would dig deeper into the compilation of the main module, assuming lib is already compiled.
After frontend execution, we get something like that. Here we have all calls resolved. Functions from dependencies (lib) are loaded as LazyIr.
fun foo() : Unit { run( lambda@{ return@lambda 42 } ) } // dependencies: // lazyIR without bodies fun process(x: Int): Unit inline fun run(block: Function0<Int>) // ... lazyIR for Int, Unit, Function and so on
Then, pre-inline lowering happens. But as we have a very simple example, there is nothing to do.
Next, we need to load Ir of run function. For that we need to run Deserializer. But we can‘t run linker, so references inside function body wouldn’t be resolved.
// IrFunction // name = run // isInline = true // valueParameter0 // irType // classifier = Lazy class kotlin.Function0 (from Lazy run function) // typeArgument0 = Lazy class kotlin.Int (from Lazy run function) // returnType = Lazy class kotlin.Unit (from Lazy run function) // body // IrCall symbol = Unbound function symbol with signature "process(Int) : Unit" // returnType = IrType classifier = Unbound class symbol with singnature kotlin.Unit // valueArgument0 = // IrCall symbol = Unbound function symbol with signature Function0.invoke() // typeArgument0 = IrType classifier = Unbound class symbol with singnature kotlin.Int // valueArguemnt0 = IrGet valueParamenter block // type = Unbound class symbol with singnature kotlin.Int // inline fun run(block: Function0<Int>) : Unit { // note, that here we merged LazyIr of run function with deserialized body process(block.invoke()) }
Now this function can be inlined to original.
// IrFunction // name = foo // returnType = Lazy class kotlin.Unit // body // IrReturnableBlock symbol=symbol1 // type = Lazy class kotlin.Unit // IrInlinedFunctionBlock required for debug information // IrReturn // target=symbol1 // value = IrCall // symbol = Unbound function symbol with signature "process(Int) : Unit" // returnType = IrType classifier = Unbound class symbol with singnature kotlin.Unit // valueArgument0 // IrReturnableBlock symbol=symbol2 // type = Lazy class for kotlin.Int // IrInlinedFunctionBlock required for debug information // IrReturn target=symbol2 value = IrConst<Int>(42) fun foo() : Unit { inlinedBlock@{ return@inlinedBlock process(lambda@ { return@lambda 42 }) } }
Several side notes on the result:
Unbound function symbol with signature Function0.invoke(), this must be done by signature only.