From e169383f763b20c729eff3605e16663ab8e7b7ac Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Wed, 10 Jan 2018 20:07:16 +0300 Subject: [PATCH] Fix problem with transforming suspend lambda instantiation by itself #KT-21605: Fixed --- .../kotlin/codegen/inline/MethodInliner.kt | 19 ++- .../crossInlineWithCapturedOuterReceiver.kt | 131 ++++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 + .../codegen/BlackBoxCodegenTestGenerated.java | 6 + .../LightAnalysisModeTestGenerated.java | 6 + .../semantics/JsCodegenBoxTestGenerated.java | 6 + 6 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index c5a07593f8d..482aaeea995 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -282,11 +282,26 @@ class MethodInliner( info = oldInfo } + val isContinuationCreate = isContinuation && oldInfo != null && resultNode.name == "create" && + resultNode.desc.startsWith("(" + CONTINUATION_ASM_TYPE.descriptor) + for (capturedParamDesc in info.allRecapturedParameters) { - visitFieldInsn( + if (capturedParamDesc.fieldName == THIS && isContinuationCreate) { + // Common inliner logic doesn't support cases when transforming anonymous object can + // be instantiated by itself. + // To support such cases workaround with 'oldInfo' is used. + // But it corresponds to outer context and a bit inapplicable for nested 'create' method context. + // 'This' in outer context corresponds to outer instance in current + visitFieldInsn( + Opcodes.GETSTATIC, owner, + CAPTURED_FIELD_FOLD_PREFIX + THIS_0, capturedParamDesc.type.descriptor + ) + } else { + visitFieldInsn( Opcodes.GETSTATIC, capturedParamDesc.containingLambdaName, CAPTURED_FIELD_FOLD_PREFIX + capturedParamDesc.fieldName, capturedParamDesc.type.descriptor - ) + ) + } } super.visitMethodInsn(opcode, info.newClassName, name, info.newConstructorDescriptor, itf) diff --git a/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt b/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt new file mode 100644 index 00000000000..f3365e0227e --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt @@ -0,0 +1,131 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import helpers.* +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +interface Consumer { fun consume(s: String) } + +inline fun crossInlineBuilderConsumer(crossinline block: (String) -> Unit) = object : Consumer { + override fun consume(s: String) { + block(s) + } +} + +inline fun inlineBuilder(block: () -> Consumer) = block() + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun builderConsumer(c: suspend () -> Consumer): Consumer { + var res: Consumer? = null + c.startCoroutine(object : Continuation { + override fun resume(value: Consumer) { + res = value + } + + override fun resumeWithException(e: Throwable) { + throw e + } + + override val context = EmptyCoroutineContext + }) + return res!! +} + +class Container { + var y: String = "FAIL 1" + + val consumer1 = crossInlineBuilderConsumer { s -> + builder { + y = s + } + } + + val consumer2 = inlineBuilder { + object : Consumer { + override fun consume(s: String) { + builder { + y = s + } + } + } + } + + val consumer3 = inlineBuilder { + builderConsumer { + object : Consumer { + override fun consume(s: String) { + y = s + } + } + } + } + + val consumer4 = crossInlineBuilderConsumer { s -> + object : Consumer { + override fun consume(s1: String) { + builder { + y = s1 + } + } + } + } + + val consumer5 = crossInlineBuilderConsumer { s -> + builderConsumer { + object : Consumer { + override fun consume(s1: String) { + y = s1 + } + } + } + } + + val consumer6 = crossInlineBuilderConsumer { s -> + val c = object : Consumer { + override fun consume(s1: String) { + builder { + y = s1 + } + } + } + c.consume(s) + } + + val consumer7 = crossInlineBuilderConsumer { s -> + val c = builderConsumer { + object : Consumer { + override fun consume(s1: String) { + y = s1 + } + } + } + c.consume(s) + } +} + +fun box(): String { + val c = Container() + c.consumer1.consume("OK") + if (c.y != "OK") return c.y + c.y = "FAIL 2" + c.consumer2.consume("OK") + if (c.y != "OK") return c.y + c.y = "FAIL 3" + c.consumer3.consume("OK") + if (c.y != "OK") return c.y + c.y = "OK" + c.consumer4.consume("FAIL 4") + if (c.y != "OK") return c.y + c.consumer5.consume("FAIL 5") + if (c.y != "OK") return c.y + c.y = "FAIL 6" + c.consumer6.consume("OK") + if (c.y != "OK") return c.y + c.y = "FAIL 7" + c.consumer7.consume("OK") + if (c.y != "OK") return c.y + return "OK" +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 3e484fa03e9..770fbd2cfb2 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5787,6 +5787,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("crossInlineWithCapturedOuterReceiver.kt") + public void testCrossInlineWithCapturedOuterReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt"); + doTest(fileName); + } + @TestMetadata("defaultParametersInSuspend.kt") public void testDefaultParametersInSuspend() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b3b4fee2d4f..6f15326f3f7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5787,6 +5787,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("crossInlineWithCapturedOuterReceiver.kt") + public void testCrossInlineWithCapturedOuterReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt"); + doTest(fileName); + } + @TestMetadata("defaultParametersInSuspend.kt") public void testDefaultParametersInSuspend() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 7d93690cc44..1c19dfc7e7e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5787,6 +5787,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("crossInlineWithCapturedOuterReceiver.kt") + public void testCrossInlineWithCapturedOuterReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt"); + doTest(fileName); + } + @TestMetadata("defaultParametersInSuspend.kt") public void testDefaultParametersInSuspend() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt"); 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 c85ceb4ccb5..8d31b021adc 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 @@ -6387,6 +6387,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that."); } + @TestMetadata("crossInlineWithCapturedOuterReceiver.kt") + public void testCrossInlineWithCapturedOuterReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt"); + doTest(fileName); + } + @TestMetadata("defaultParametersInSuspend.kt") public void testDefaultParametersInSuspend() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");