JVM IR: fix handling of suspend extension lambdas with captured receiver

The problem in the added test was that a suspend lambda was represented
by a function reference with a bound argument for the ObjectRef value,
and the corresponding parameter was not the first parameter of the
referenced local function. This happens because
LocalDeclarationsLowering lifts the local function up and adds a
new parameter for the captured ObjectRef (which is bound at the call
site), but the original receiver parameter remains the first unbound
parameter. So, it's no longer correct to rely on the fact that all bound
parameters of a function reference are located in the beginning of the
parameter list, which was kind of assumed in the `withIndex` call in
`AddContinuationLowering.addCreate`.
This commit is contained in:
Alexander Udalov
2020-01-14 21:03:37 +01:00
parent f45ca7acd3
commit 6fe214d825
8 changed files with 62 additions and 5 deletions
@@ -121,8 +121,10 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
val parametersFields = info.function.valueParameters.map { addField(it.name, it.type) }
val parametersWithoutArguments = parametersFields.withIndex()
.mapNotNull { (i, field) -> if (info.reference.getValueArgument(i) == null) field else null }
val parametersWithArguments = parametersFields - parametersWithoutArguments
val constructor = addPrimaryConstructorForLambda(info.arity, info.reference, parametersFields)
val parametersWithArguments = parametersFields.withIndex()
.filter { info.reference.getValueArgument(it.index) != null }
val fieldsForArguments = parametersWithArguments.map(IndexedValue<IrField>::value)
val constructor = addPrimaryConstructorForLambda(info.arity, info.reference, fieldsForArguments)
val invokeToOverride = functionNClass.functions.single {
it.owner.valueParameters.size == info.arity + 1 && it.owner.name.asString() == "invoke"
}
@@ -140,7 +142,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
constructor,
invokeSuspend,
invokeToOverride,
parametersWithArguments,
fieldsForArguments,
listOfNotNull(receiverField) + parametersWithoutArguments
)
}
@@ -308,7 +310,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
constructor: IrFunction,
superType: IrClass,
info: SuspendLambdaInfo,
parametersWithArguments: List<IrField>,
parametersWithArguments: List<IndexedValue<IrField>>,
singleParameterField: IrField?
): IrFunction {
val create = superType.functions.single {
@@ -323,7 +325,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
for (typeParameter in typeParameters) {
it.putTypeArgument(typeParameter.index, typeParameter.defaultType)
}
for ((i, field) in parametersWithArguments.withIndex()) {
for ((i, field) in parametersWithArguments) {
if (info.reference.getValueArgument(i) == null) continue
it.putValueArgument(index++, irGetField(irGet(function.dispatchReceiverParameter!!), field))
}
@@ -0,0 +1,25 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
fun builder(c: suspend String.() -> Unit) {
c.startCoroutine("OK", EmptyContinuation)
}
interface A {
var result: String
fun test(): String {
builder { result = this }
return result
}
}
fun box(): String =
object : A {
override var result = "Fail"
}.test()
@@ -7150,6 +7150,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
@@ -7150,6 +7150,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
@@ -6565,6 +6565,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines");
@@ -6565,6 +6565,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines");
@@ -5580,6 +5580,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines");
@@ -6125,6 +6125,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
}
@TestMetadata("interfaceMethodWithBody.kt")
public void testInterfaceMethodWithBody() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt");
}
@TestMetadata("safeCallOnTwoReceiversLong.kt")
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");