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 {
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
@@ -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)) }
@@ -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) {
@@ -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
@@ -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 {
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
@@ -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
@@ -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);
}
@@ -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
@@ -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
@@ -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")
@@ -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