From 6fe214d825863576e4537952080fb2aeda92899f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 14 Jan 2020 21:03:37 +0100 Subject: [PATCH] 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`. --- .../jvm/lower/AddContinuationLowering.kt | 12 +++++---- .../interfaceMethodWithBody.kt | 25 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++++ .../LightAnalysisModeTestGenerated.java | 5 ++++ .../ir/FirBlackBoxCodegenTestGenerated.java | 5 ++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++++ .../IrJsCodegenBoxTestGenerated.java | 5 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++++ 8 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt index 4d42e89158e..64401f35c08 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -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::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, + parametersWithArguments: List>, 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)) } diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt new file mode 100644 index 00000000000..8bd92cef3c5 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/interfaceMethodWithBody.kt @@ -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() diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 2ef5c00e70d..54312c15332 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 1c64326cec7..2c1805314db 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index c1eba7cc0e9..362758dc61e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4a3e50f9754..029ce75627d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index a64cb5a4ab3..b4e48ecb32e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index c039679f486..b033cca244f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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");