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
This commit is contained in:
pyos
2022-09-16 09:47:54 +02:00
committed by Alexander Udalov
parent 309c57f860
commit 2dd49e5fb4
12 changed files with 90 additions and 13 deletions
@@ -11284,6 +11284,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested
@@ -66,6 +66,9 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
protected open fun getWrappedFunctionType(klass: IrClass): IrType = protected open fun getWrappedFunctionType(klass: IrClass): IrType =
klass.defaultType klass.defaultType
protected open fun getSuspendFunctionWithoutContinuation(function: IrSimpleFunction): IrSimpleFunction =
function
protected open fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) { protected open fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) {
setSourceRange(createFor) setSourceRange(createFor)
} }
@@ -156,15 +159,16 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_') val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_')
val inlinePrefix = if (wrapperVisibility == DescriptorVisibilities.PUBLIC) "\$i" else "" val inlinePrefix = if (wrapperVisibility == DescriptorVisibilities.PUBLIC) "\$i" else ""
val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX") val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX")
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT } val transformedSuperMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
val extensionReceiversCount = if (superMethod.extensionReceiverParameter == null) 0 else 1 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 // TODO: have psi2ir cast the argument to the correct function type. Also see the TODO
// about type parameters in `visitTypeOperator`. // about type parameters in `visitTypeOperator`.
val wrappedFunctionClass = val wrappedFunctionClass =
if (superMethod.isSuspend) if (originalSuperMethod.isSuspend)
context.ir.symbols.suspendFunctionN(superMethod.valueParameters.size + extensionReceiversCount).owner context.ir.symbols.suspendFunctionN(originalSuperMethod.valueParameters.size + extensionReceiversCount).owner
else else
context.ir.symbols.functionN(superMethod.valueParameters.size + extensionReceiversCount).owner context.ir.symbols.functionN(originalSuperMethod.valueParameters.size + extensionReceiversCount).owner
val wrappedFunctionType = getWrappedFunctionType(wrappedFunctionClass) val wrappedFunctionType = getWrappedFunctionType(wrappedFunctionClass)
val subclass = context.irFactory.buildClass { val subclass = context.irFactory.buildClass {
@@ -207,23 +211,23 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
} }
subclass.addFunction { subclass.addFunction {
name = superMethod.name name = originalSuperMethod.name
returnType = superMethod.returnType returnType = originalSuperMethod.returnType
visibility = superMethod.visibility visibility = originalSuperMethod.visibility
modality = Modality.FINAL modality = Modality.FINAL
origin = IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION origin = IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION
isSuspend = superMethod.isSuspend isSuspend = originalSuperMethod.isSuspend
setSourceRange(createFor) setSourceRange(createFor)
}.apply { }.apply {
overriddenSymbols = listOf(superMethod.symbol) overriddenSymbols = listOf(transformedSuperMethod.symbol)
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this) dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
extensionReceiverParameter = superMethod.extensionReceiverParameter?.copyTo(this) extensionReceiverParameter = originalSuperMethod.extensionReceiverParameter?.copyTo(this)
valueParameters = superMethod.valueParameters.map { it.copyTo(this) } valueParameters = originalSuperMethod.valueParameters.map { it.copyTo(this) }
body = context.createIrBuilder(symbol).irBlockBody { body = context.createIrBuilder(symbol).irBlockBody {
+irReturn( +irReturn(
irCall( irCall(
wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }.symbol, wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }.symbol,
superMethod.returnType originalSuperMethod.returnType
).apply { ).apply {
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field) dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)
extensionReceiverParameter?.let { putValueArgument(0, irGet(it)) } extensionReceiverParameter?.let { putValueArgument(0, irGet(it)) }
@@ -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.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope
import org.jetbrains.kotlin.backend.jvm.ir.rawType 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.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrElement 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.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin 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.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
@@ -51,6 +53,9 @@ private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : Sing
override fun getWrappedFunctionType(klass: IrClass): IrType = override fun getWrappedFunctionType(klass: IrClass): IrType =
klass.rawType(context as JvmBackendContext) 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. // The constructor of a SAM wrapper is non-synthetic and should not have line numbers.
// Otherwise the debugger will try to step into it. // Otherwise the debugger will try to step into it.
override fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) { override fun IrFunctionBuilder.setConstructorSourceRange(createFor: IrElement) {
@@ -67,6 +67,7 @@ private val BRIDGE_ORIGINS = setOf(
JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE, JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE,
IrDeclarationOrigin.BRIDGE, IrDeclarationOrigin.BRIDGE,
IrDeclarationOrigin.BRIDGE_SPECIAL, 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 // These functions contain a single `suspend` tail call, the value of which should be returned as is
@@ -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"
}
@@ -11116,6 +11116,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested
@@ -11284,6 +11284,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested
@@ -8804,6 +8804,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 { private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
} }
@@ -8010,6 +8010,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested
@@ -8100,6 +8100,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested
@@ -7131,6 +7131,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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") @TestMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/jvmDefault")
@@ -8980,6 +8980,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
public void testKt49294() throws Exception { public void testKt49294() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/funInterface/kt49294.kt"); 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 @Nested