From 2dd49e5fb42e55070350521bf23a8e1ba4907f01 Mon Sep 17 00:00:00 2001 From: pyos Date: Fri, 16 Sep 2022 09:47:54 +0200 Subject: [PATCH] JVM_IR: unwrap suspend views when generating SAM wrappers If the super class is in a file that has already been lowered, the base method has an extra continuation parameter which breaks things. Also, SAM wrappers around functional objects are tail-call and do not need continuations ever, so don't even try. ^KT-50950 Fixed --- .../FirBlackBoxCodegenTestGenerated.java | 6 ++++ .../lower/SingleAbstractMethodLowering.kt | 30 +++++++++++-------- .../lower/JvmSingleAbstractMethodLowering.kt | 5 ++++ .../backend/jvm/ir/JvmIrCoroutineUtils.kt | 1 + .../funInterface/kt50950.kt | 21 +++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../IrBlackBoxCodegenTestGenerated.java | 6 ++++ .../LightAnalysisModeTestGenerated.java | 5 ++++ .../js/test/JsCodegenBoxTestGenerated.java | 6 ++++ .../test/ir/IrJsCodegenBoxTestGenerated.java | 6 ++++ .../IrCodegenBoxWasmTestGenerated.java | 5 ++++ .../NativeCodegenBoxTestGenerated.java | 6 ++++ 12 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 67d970ddc57..1a1ad737eef 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -11284,6 +11284,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt index 292d16508bf..335e811646b 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt @@ -66,6 +66,9 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) : protected open fun getWrappedFunctionType(klass: IrClass): IrType = klass.defaultType + protected open fun getSuspendFunctionWithoutContinuation(function: IrSimpleFunction): IrSimpleFunction = + function + protected open fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) { setSourceRange(createFor) } @@ -156,15 +159,16 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) : val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_') val inlinePrefix = if (wrapperVisibility == DescriptorVisibilities.PUBLIC) "\$i" else "" val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX") - val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT } - val extensionReceiversCount = if (superMethod.extensionReceiverParameter == null) 0 else 1 + val transformedSuperMethod = superClass.functions.single { it.modality == Modality.ABSTRACT } + val originalSuperMethod = getSuspendFunctionWithoutContinuation(transformedSuperMethod) + val extensionReceiversCount = if (originalSuperMethod.extensionReceiverParameter == null) 0 else 1 // TODO: have psi2ir cast the argument to the correct function type. Also see the TODO // about type parameters in `visitTypeOperator`. val wrappedFunctionClass = - if (superMethod.isSuspend) - context.ir.symbols.suspendFunctionN(superMethod.valueParameters.size + extensionReceiversCount).owner + if (originalSuperMethod.isSuspend) + context.ir.symbols.suspendFunctionN(originalSuperMethod.valueParameters.size + extensionReceiversCount).owner else - context.ir.symbols.functionN(superMethod.valueParameters.size + extensionReceiversCount).owner + context.ir.symbols.functionN(originalSuperMethod.valueParameters.size + extensionReceiversCount).owner val wrappedFunctionType = getWrappedFunctionType(wrappedFunctionClass) val subclass = context.irFactory.buildClass { @@ -207,23 +211,23 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) : } subclass.addFunction { - name = superMethod.name - returnType = superMethod.returnType - visibility = superMethod.visibility + name = originalSuperMethod.name + returnType = originalSuperMethod.returnType + visibility = originalSuperMethod.visibility modality = Modality.FINAL origin = IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION - isSuspend = superMethod.isSuspend + isSuspend = originalSuperMethod.isSuspend setSourceRange(createFor) }.apply { - overriddenSymbols = listOf(superMethod.symbol) + overriddenSymbols = listOf(transformedSuperMethod.symbol) dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this) - extensionReceiverParameter = superMethod.extensionReceiverParameter?.copyTo(this) - valueParameters = superMethod.valueParameters.map { it.copyTo(this) } + extensionReceiverParameter = originalSuperMethod.extensionReceiverParameter?.copyTo(this) + valueParameters = originalSuperMethod.valueParameters.map { it.copyTo(this) } body = context.createIrBuilder(symbol).irBlockBody { +irReturn( irCall( wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }.symbol, - superMethod.returnType + originalSuperMethod.returnType ).apply { dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field) extensionReceiverParameter?.let { putValueArgument(0, irGet(it)) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSingleAbstractMethodLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSingleAbstractMethodLowering.kt index f76fc5c88a4..4bece618f23 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSingleAbstractMethodLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSingleAbstractMethodLowering.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.ir.rawType +import org.jetbrains.kotlin.backend.jvm.ir.suspendFunctionOriginal import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.IrElement @@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.IrFunctionBuilder import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.getClass @@ -51,6 +53,9 @@ private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : Sing override fun getWrappedFunctionType(klass: IrClass): IrType = klass.rawType(context as JvmBackendContext) + override fun getSuspendFunctionWithoutContinuation(function: IrSimpleFunction): IrSimpleFunction = + function.suspendFunctionOriginal() + // The constructor of a SAM wrapper is non-synthetic and should not have line numbers. // Otherwise the debugger will try to step into it. override fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt index aac978bbcc6..48d059260a8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt @@ -67,6 +67,7 @@ private val BRIDGE_ORIGINS = setOf( JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE, IrDeclarationOrigin.BRIDGE, IrDeclarationOrigin.BRIDGE_SPECIAL, + IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION, ) // These functions contain a single `suspend` tail call, the value of which should be returned as is diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt new file mode 100644 index 00000000000..365cff9fe62 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt @@ -0,0 +1,21 @@ +// IGNORE_BACKEND: JVM +// WITH_COROUTINES +// WITH_STDLIB +// FILE: a.kt +fun interface A { + suspend fun run() +} + +// FILE: b.kt +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* +import helpers.EmptyContinuation + +suspend fun run(block: suspend () -> Unit) = A(block).run() + +fun box(): String { + suspend { + run {} + }.startCoroutine(EmptyContinuation) + return "OK" +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index e73725d5eee..6d853e45134 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -11116,6 +11116,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index bb4b4fbe6ff..a3e1fff4ec8 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -11284,6 +11284,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index ac812dd856e..fd545128770 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -8804,6 +8804,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + @TestMetadata("kt50950.kt") + public void ignoreKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index 521246acdcf..78460269b3b 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -8010,6 +8010,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index a76a0d1728b..234388108e7 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -8100,6 +8100,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 1d56e37be68..5c3e3f80456 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -7131,6 +7131,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/jvmDefault") diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index c1c1b2c85e0..f31b4b99197 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -8980,6 +8980,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest public void testKt49294() throws Exception { runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); } + + @Test + @TestMetadata("kt50950.kt") + public void testKt50950() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt50950.kt"); + } } @Nested