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
@@ -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