Proper resort variables on inlining lowered ir closures
Original problem is that lowered ir closures doesn't meet inliner expectations
about captured variable position in inlining method.
E.g.: Call 'foo(valueParam) { capturedParam }' to
inline function 'foo' with declaration
inline fun foo(valueParam: Foo, inlineParamWithCaptured: Bar.() ->) ....
is reorganized through inlining to equivalent call foo(valueParam, capturedParam1, cp2 ...).
But lowered closure for lambda parameter has totally different parameters order:
fun loweredLambda$x(extensionReceiver, captured1, cp2..., valueParam1, vp2...)
So before inlining lowered closure should be transformed to
fun loweredLambda$x(extensionReceiver, valueParam1, vp2..., captured1, cp2..)
#KT-28547 Fixed
This commit is contained in:
@@ -418,12 +418,15 @@ class MethodInliner(
|
|||||||
private val isInliningLambda = nodeRemapper.isInsideInliningLambda
|
private val isInliningLambda = nodeRemapper.isInsideInliningLambda
|
||||||
|
|
||||||
private fun getNewIndex(`var`: Int): Int {
|
private fun getNewIndex(`var`: Int): Int {
|
||||||
if (inliningContext.isInliningLambda && inliningContext.lambdaInfo is IrExpressionLambda) {
|
val lambdaInfo = inliningContext.lambdaInfo
|
||||||
|
if (inliningContext.isInliningLambda && lambdaInfo is IrExpressionLambda) {
|
||||||
if (`var` < parameters.argsSizeOnStack) {
|
if (`var` < parameters.argsSizeOnStack) {
|
||||||
if (`var` < capturedParamsSize) {
|
val capturedParamsStartIndex =
|
||||||
return `var` + realParametersSize
|
if (lambdaInfo.isExtensionLambda) lambdaInfo.invokeMethod.argumentTypes[0].size else 0 //shift by extension
|
||||||
}
|
val capturedParamsEndIndex = capturedParamsSize + capturedParamsStartIndex - 1
|
||||||
else {
|
if (`var` in capturedParamsStartIndex..capturedParamsEndIndex) {
|
||||||
|
return `var` + realParametersSize - capturedParamsStartIndex //subtract extension
|
||||||
|
} else if (`var` >= capturedParamsStartIndex) {
|
||||||
return `var` - capturedParamsSize
|
return `var` - capturedParamsSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-18
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
||||||
import org.jetbrains.kotlin.codegen.Callable
|
import org.jetbrains.kotlin.codegen.Callable
|
||||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
@@ -34,9 +35,8 @@ class IrInlineCodegen(
|
|||||||
val lambdaInfo = next as IrExpressionLambda
|
val lambdaInfo = next as IrExpressionLambda
|
||||||
activeLambda = lambdaInfo
|
activeLambda = lambdaInfo
|
||||||
|
|
||||||
val argumentTypes = lambdaInfo.loweredMethod.argumentTypes
|
|
||||||
lambdaInfo.reference.getArguments().forEachIndexed { index, (_, ir) ->
|
lambdaInfo.reference.getArguments().forEachIndexed { index, (_, ir) ->
|
||||||
putCapturedValueOnStack(ir, argumentTypes[index], index)
|
putCapturedValueOnStack(ir, lambdaInfo.capturedParamsInDesc[index], index)
|
||||||
}
|
}
|
||||||
activeLambda = null
|
activeLambda = null
|
||||||
}
|
}
|
||||||
@@ -70,10 +70,10 @@ class IrInlineCodegen(
|
|||||||
putArgumentOrCapturedToLocalVal(JvmKotlinType(value.type, value.kotlinType), value, -1, parameterIndex, ValueKind.CAPTURED /*kind*/)
|
putArgumentOrCapturedToLocalVal(JvmKotlinType(value.type, value.kotlinType), value, -1, parameterIndex, ValueKind.CAPTURED /*kind*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun putCapturedValueOnStack(argumentExpression: IrExpression, valueType: Type, capturedParamindex: Int) {
|
private fun putCapturedValueOnStack(argumentExpression: IrExpression, valueType: Type, capturedParamIndex: Int) {
|
||||||
val onStack = codegen.gen(argumentExpression, valueType, BlockInfo.create())
|
val onStack = codegen.gen(argumentExpression, valueType, BlockInfo.create())
|
||||||
putArgumentOrCapturedToLocalVal(
|
putArgumentOrCapturedToLocalVal(
|
||||||
JvmKotlinType(onStack.type, onStack.kotlinType), onStack, capturedParamindex, capturedParamindex, ValueKind.CAPTURED
|
JvmKotlinType(onStack.type, onStack.kotlinType), onStack, capturedParamIndex, capturedParamIndex, ValueKind.CAPTURED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +93,10 @@ class IrInlineCodegen(
|
|||||||
|
|
||||||
private fun rememberClosure(irReference: IrFunctionReference, type: Type, parameter: ValueParameterDescriptor): LambdaInfo {
|
private fun rememberClosure(irReference: IrFunctionReference, type: Type, parameter: ValueParameterDescriptor): LambdaInfo {
|
||||||
//assert(InlineUtil.isInlinableParameterExpression(ktLambda)) { "Couldn't find inline expression in ${expression.text}" }
|
//assert(InlineUtil.isInlinableParameterExpression(ktLambda)) { "Couldn't find inline expression in ${expression.text}" }
|
||||||
|
|
||||||
val expression = irReference.symbol.owner as IrFunction
|
val expression = irReference.symbol.owner as IrFunction
|
||||||
return IrExpressionLambda(
|
return IrExpressionLambda(
|
||||||
irReference, expression, typeMapper, parameter.isCrossinline, false/*TODO*/
|
irReference, expression, typeMapper, parameter.isCrossinline, false/*TODO*/,
|
||||||
|
parameter.type.isExtensionFunctionType
|
||||||
).also { lambda ->
|
).also { lambda ->
|
||||||
val closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.index)
|
val closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.index)
|
||||||
closureInfo.lambda = lambda
|
closureInfo.lambda = lambda
|
||||||
@@ -110,7 +110,8 @@ class IrExpressionLambda(
|
|||||||
val function: IrFunction,
|
val function: IrFunction,
|
||||||
typeMapper: KotlinTypeMapper,
|
typeMapper: KotlinTypeMapper,
|
||||||
isCrossInline: Boolean,
|
isCrossInline: Boolean,
|
||||||
override val isBoundCallableReference: Boolean
|
override val isBoundCallableReference: Boolean,
|
||||||
|
val isExtensionLambda: Boolean
|
||||||
) : ExpressionLambda(typeMapper, isCrossInline) {
|
) : ExpressionLambda(typeMapper, isCrossInline) {
|
||||||
|
|
||||||
override fun isMyLabel(name: String): Boolean {
|
override fun isMyLabel(name: String): Boolean {
|
||||||
@@ -118,30 +119,35 @@ class IrExpressionLambda(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override val lambdaClassType: Type
|
override val lambdaClassType: Type = Type.getObjectType("test123")
|
||||||
get() = Type.getObjectType("test123")
|
|
||||||
|
|
||||||
override val capturedVars: List<CapturedParamDesc> by lazy {
|
override val capturedVars: List<CapturedParamDesc> =
|
||||||
arrayListOf<CapturedParamDesc>().apply {
|
arrayListOf<CapturedParamDesc>().apply {
|
||||||
reference.getArguments().forEachIndexed { _, (_, ir) ->
|
reference.getArguments().forEachIndexed { _, (_, ir) ->
|
||||||
val getValue = ir as? IrGetValue ?: error("Unrecognized expression: $ir")
|
val getValue = ir as? IrGetValue ?: error("Unrecognized expression: $ir")
|
||||||
add(capturedParamDesc(getValue.descriptor.name.asString(), typeMapper.mapType(getValue.descriptor.type)))
|
add(capturedParamDesc(getValue.descriptor.name.asString(), typeMapper.mapType(getValue.descriptor.type)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val loweredMethod: Method
|
private val loweredMethod = typeMapper.mapAsmMethod(function.descriptor)
|
||||||
get() = typeMapper.mapAsmMethod(function.descriptor)
|
|
||||||
|
val capturedParamsInDesc: List<Type> =
|
||||||
|
loweredMethod.argumentTypes.drop(if (isExtensionLambda) 1 else 0).take(capturedVars.size)
|
||||||
|
|
||||||
override val invokeMethod: Method = loweredMethod.let {
|
override val invokeMethod: Method = loweredMethod.let {
|
||||||
Method(it.name, it.returnType, it.argumentTypes.drop(capturedVars.size).toTypedArray())
|
Method(
|
||||||
|
it.name,
|
||||||
|
it.returnType,
|
||||||
|
(
|
||||||
|
(if (isExtensionLambda) it.argumentTypes.take(1) else emptyList()) +
|
||||||
|
it.argumentTypes.drop((if (isExtensionLambda) 1 else 0) + capturedVars.size)
|
||||||
|
).toTypedArray()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val invokeMethodDescriptor: FunctionDescriptor
|
override val invokeMethodDescriptor: FunctionDescriptor = function.descriptor
|
||||||
get() = function.descriptor
|
|
||||||
|
|
||||||
override val hasDispatchReceiver: Boolean
|
override val hasDispatchReceiver: Boolean = false
|
||||||
get() = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isInlineIrExpression(argumentExpression: IrExpression) =
|
fun isInlineIrExpression(argumentExpression: IrExpression) =
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
public inline fun <T> T.with(f: T.() -> Unit): T {
|
public inline fun <T> T.with(f: T.() -> Unit): T {
|
||||||
this.f()
|
this.f()
|
||||||
return this
|
return this
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
class Foo {
|
class Foo {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// NO_CHECK_LAMBDA_INLINING
|
// NO_CHECK_LAMBDA_INLINING
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
package test
|
package test
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
inline fun <T> run(c: () -> T): T = c()
|
inline fun <T> run(c: () -> T): T = c()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
package test
|
package test
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
package test
|
||||||
|
|
||||||
|
class C {
|
||||||
|
var inserting: Boolean = false
|
||||||
|
fun nextSlot(): Any? = null
|
||||||
|
fun startNode(key: Any?) {}
|
||||||
|
fun endNode() {}
|
||||||
|
fun emitNode(node: Any?) {}
|
||||||
|
fun useNode(): Any? = null
|
||||||
|
fun skipValue() {}
|
||||||
|
fun updateValue(value: Any?) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B<T>(val composer: C, val node: T) {
|
||||||
|
inline fun <V> bar(value: V, block: T.(V) -> Unit) = with(composer) {
|
||||||
|
if (inserting || nextSlot() != value) {
|
||||||
|
updateValue(value)
|
||||||
|
node.block(value)
|
||||||
|
} else skipValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A(val composer: C) {
|
||||||
|
inline fun <T> foo(key: Any, ctor: () -> T, update: B<T>.() -> Unit) = with(composer) {
|
||||||
|
startNode(key)
|
||||||
|
val node = if (inserting)
|
||||||
|
ctor().also { emitNode(it) }
|
||||||
|
else useNode() as T
|
||||||
|
B<T>(this, node).update()
|
||||||
|
endNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a = A(C())
|
||||||
|
val str = "OK"
|
||||||
|
var result = "fail"
|
||||||
|
a.foo<String>(
|
||||||
|
123,
|
||||||
|
{ "abc" },
|
||||||
|
{
|
||||||
|
bar(str) { }
|
||||||
|
result = "OK"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
inline fun Double.run(body : Double.() -> String): String {
|
||||||
|
return this.body()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var captured = "fail"
|
||||||
|
return 1.0.run {
|
||||||
|
if (this == 1.0) {
|
||||||
|
"OK"
|
||||||
|
} else captured
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|||||||
+10
@@ -2813,6 +2813,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547.kt")
|
||||||
|
public void testKt28547() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547_2.kt")
|
||||||
|
public void testKt28547_2() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547_2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("params.kt")
|
@TestMetadata("params.kt")
|
||||||
public void testParams() throws Exception {
|
public void testParams() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
||||||
|
|||||||
Generated
+10
@@ -2813,6 +2813,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547.kt")
|
||||||
|
public void testKt28547() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547_2.kt")
|
||||||
|
public void testKt28547_2() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547_2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("params.kt")
|
@TestMetadata("params.kt")
|
||||||
public void testParams() throws Exception {
|
public void testParams() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
||||||
|
|||||||
+10
@@ -2813,6 +2813,16 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
|||||||
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/kt17431.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547.kt")
|
||||||
|
public void testKt28547() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt28547_2.kt")
|
||||||
|
public void testKt28547_2() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxInline/simple/kt28547_2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("params.kt")
|
@TestMetadata("params.kt")
|
||||||
public void testParams() throws Exception {
|
public void testParams() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
runTest("compiler/testData/codegen/boxInline/simple/params.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user