From a55989a2a52ff924c8f254d360736439bd4dd182 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Tue, 21 Jan 2020 13:47:58 +0100 Subject: [PATCH] JVM_IR: Support interface delegation of suspend functions The issue was, that built IR function does not have a PSI element, which is required to report error on suspend functions inside monitors. In this case, use PSI element of the class, containing the function, which is consistent with old BE. --- .../backend/jvm/codegen/FunctionCodegen.kt | 10 ++-- .../interfaceDelegation.kt | 56 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../ir/FirBlackBoxCodegenTestGenerated.java | 5 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ 6 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index 4da35f03929..210ebae33fd 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -14,9 +14,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_IMPL_NAME_SUFFIX import org.jetbrains.kotlin.codegen.mangleNameIfNeeded import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.codegen.ClassBuilderMode -import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_IMPL_NAME_SUFFIX -import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.Modality @@ -122,8 +119,11 @@ open class FunctionCodegen( isAnonymousObject || origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS || origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA private fun psiElement(): KtElement = - if (irFunction.isSuspend) irFunction.symbol.descriptor.psiElement as KtElement - else context.suspendLambdaToOriginalFunctionMap[irFunction.parentAsClass.attributeOwnerId]!!.symbol.descriptor.psiElement as KtElement + (if (irFunction.isSuspend) + irFunction.symbol.descriptor.psiElement ?: irFunction.parentAsClass.descriptor.psiElement + else + context.suspendLambdaToOriginalFunctionMap[irFunction.parentAsClass.attributeOwnerId]!!.symbol.descriptor.psiElement) + as KtElement private fun IrFunction.hasContinuation(): Boolean = isSuspend && // We do not generate continuation and state-machine for synthetic accessors, bridges, and delegated members, diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt new file mode 100644 index 00000000000..5ad7c1cdeb3 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt @@ -0,0 +1,56 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendThere(v: String): String = suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED +} + +interface I { + suspend fun suspendHere(): String + + suspend fun suspendHereNoTailCall(): String +} + +class A : I { + override suspend fun suspendHere(): String = suspendThere("OK") + + override suspend fun suspendHereNoTailCall(): String { + suspendThere("FAIL 2") + return "OK" + } +} + +open class B(val x: I) : I by x // open override suspend fun suspendHere() = x.suspendHere() + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = B(A()).suspendHere() + } + TailCallOptimizationChecker.checkNoStateMachineIn("suspendHere") + TailCallOptimizationChecker.checkNoStateMachineIn("suspendHere\$suspendImpl") + + if (result != "OK") return "FAIL 1" + + builder { + result = B(A()).suspendHereNoTailCall() + } + TailCallOptimizationChecker.checkStateMachineIn("suspendHereNoTailCall") + TailCallOptimizationChecker.checkNoStateMachineIn("suspendHereNoTailCall\$suspendImpl") + + return result +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 7c5650be0fe..eb7c5788d7d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -8612,6 +8612,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); } + @TestMetadata("interfaceDelegation.kt") + public void testInterfaceDelegation() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/simple.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 36a591536bf..fcd08a3ebdf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -8612,6 +8612,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); } + @TestMetadata("interfaceDelegation.kt") + public void testInterfaceDelegation() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/simple.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 45fbbd5616f..20e06673d6e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -7517,6 +7517,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); } + @TestMetadata("interfaceDelegation.kt") + public void testInterfaceDelegation() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/simple.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7042980af16..b29f7b78a4b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7517,6 +7517,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); } + @TestMetadata("interfaceDelegation.kt") + public void testInterfaceDelegation() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/simple.kt");