[K/N] Remove hack from suspend function inheritance to allow export

Before that commit, function in coroutine implementation class was
override for both SuspendFunctionN's and FunctionN+1's invoke,
which somehow works, but breaks exporting classes with such functions.

Now it's two separate functions, as it should be normally.

^KT-49395
This commit is contained in:
Pavel Kunyavskiy
2021-11-22 16:39:20 +03:00
committed by Space
parent 536bfe5bf9
commit 98fa69c3d5
6 changed files with 107 additions and 54 deletions
@@ -6,15 +6,13 @@
package org.jetbrains.kotlin.backend.common.lower package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildClass import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
@@ -35,6 +33,9 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
protected abstract val stateMachineMethodName: Name protected abstract val stateMachineMethodName: Name
protected abstract fun getCoroutineBaseClass(function: IrFunction): IrClassSymbol protected abstract fun getCoroutineBaseClass(function: IrFunction): IrClassSymbol
protected abstract fun nameForCoroutineClass(function: IrFunction): Name protected abstract fun nameForCoroutineClass(function: IrFunction): Name
protected abstract fun IrBuilderWithScope.launchSuspendFunctionWithGivenContinuation(
symbol: IrSimpleFunctionSymbol, dispatchReceiver: IrExpression,
arguments: List<IrExpression>, continuation: IrExpression) : IrExpression
protected abstract fun buildStateMachine( protected abstract fun buildStateMachine(
stateMachineFunction: IrFunction, stateMachineFunction: IrFunction,
@@ -85,22 +86,24 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
for (suspendFunctionType in suspendFunctionTypes) { for (suspendFunctionType in suspendFunctionTypes) {
val suspendFunctionClassSymbol = suspendFunctionType.classOrNull ?: continue val suspendFunctionClassSymbol = suspendFunctionType.classOrNull ?: continue
val suspendFunctionSymbol = suspendFunctionClassSymbol.owner.simpleFunctions().single { val suspendFunction = suspendFunctionClassSymbol.owner.simpleFunctions().single {
it.name == OperatorNameConventions.INVOKE it.name == OperatorNameConventions.INVOKE
}.symbol
val invokeFunction = clazz.simpleFunctions().single {
it.name == OperatorNameConventions.INVOKE && suspendFunctionSymbol in it.overriddenSymbols
} }
val suspendFunctionArity = suspendFunctionSymbol.owner.valueParameters.size val invokeFunction = clazz.simpleFunctions().single {
it.name == OperatorNameConventions.INVOKE && it.overrides(suspendFunction)
}
if (invokeFunction.modality == Modality.ABSTRACT) {
continue
}
val suspendFunctionArity = suspendFunction.valueParameters.size
val functionClassSymbol = symbols.functionN(suspendFunctionArity + 1) val functionClassSymbol = symbols.functionN(suspendFunctionArity + 1)
val functionSymbol = functionClassSymbol.owner.simpleFunctions().single { val functionSymbol = functionClassSymbol.owner.simpleFunctions().single {
it.name == OperatorNameConventions.INVOKE it.name == OperatorNameConventions.INVOKE
}.symbol }.symbol
invokeFunction.overriddenSymbols += functionSymbol
val functionClassTypeArguments = suspendFunctionType.arguments.mapIndexed { index, argument -> val functionClassTypeArguments = suspendFunctionType.arguments.mapIndexed { index, argument ->
val type = (argument as IrTypeProjection).type val type = (argument as IrTypeProjection).type
if (index == suspendFunctionArity) continuationClassSymbol.typeWith(type) else type if (index == suspendFunctionArity) continuationClassSymbol.typeWith(type) else type
@@ -109,6 +112,51 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
val functionType = functionClassSymbol.typeWith(functionClassTypeArguments) val functionType = functionClassSymbol.typeWith(functionClassTypeArguments)
clazz.superTypes += functionType clazz.superTypes += functionType
context.irFactory.buildFun {
startOffset = invokeFunction.startOffset
endOffset = invokeFunction.endOffset
origin = DECLARATION_ORIGIN_COROUTINE_IMPL
name = OperatorNameConventions.INVOKE
visibility = DescriptorVisibilities.PROTECTED
returnType = context.irBuiltIns.anyNType
}.apply {
parent = clazz
clazz.declarations += this
typeParameters = invokeFunction.typeParameters.map { parameter ->
parameter.copyToWithoutSuperTypes(this, origin = DECLARATION_ORIGIN_COROUTINE_IMPL)
.apply { superTypes += parameter.superTypes }
}
valueParameters = invokeFunction.valueParameters
.mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
valueParameters += buildValueParameter(this) {
index = valueParameters.size
type = context.irBuiltIns.anyNType
name = "completion".synthesizedName
}
this.createDispatchReceiverParameter()
overriddenSymbols += functionSymbol
val thisReceiver = dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
launchSuspendFunctionWithGivenContinuation(
invokeFunction.symbol,
irGet(thisReceiver),
valueParameters.dropLast(1).map { irGet(it) },
irGet(valueParameters.last())
)
)
}
}
} }
} }
}) })
@@ -356,9 +404,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
fun build(): BuiltCoroutine { fun build(): BuiltCoroutine {
val superTypes = mutableListOf(coroutineBaseClass.defaultType) val superTypes = mutableListOf(coroutineBaseClass.defaultType)
var suspendFunctionClass: IrClass? = null var suspendFunctionClass: IrClass? = null
var functionClass: IrClass? = null
val suspendFunctionClassTypeArguments: List<IrType>? val suspendFunctionClassTypeArguments: List<IrType>?
val functionClassTypeArguments: List<IrType>?
if (unboundFunctionParameters != null) { if (unboundFunctionParameters != null) {
// Suspend lambda inherits SuspendFunction. // Suspend lambda inherits SuspendFunction.
val numberOfParameters = unboundFunctionParameters.size val numberOfParameters = unboundFunctionParameters.size
@@ -366,10 +412,6 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
val unboundParameterTypes = unboundFunctionParameters.map { it.type } val unboundParameterTypes = unboundFunctionParameters.map { it.type }
suspendFunctionClassTypeArguments = unboundParameterTypes + irFunction.returnType suspendFunctionClassTypeArguments = unboundParameterTypes + irFunction.returnType
superTypes += suspendFunctionClass.typeWith(suspendFunctionClassTypeArguments) superTypes += suspendFunctionClass.typeWith(suspendFunctionClassTypeArguments)
functionClass = symbols.functionN(numberOfParameters + 1).owner
functionClassTypeArguments = unboundParameterTypes + continuationType + context.irBuiltIns.anyNType
superTypes += functionClass.typeWith(functionClassTypeArguments)
} }
val coroutineConstructor = buildConstructor() val coroutineConstructor = buildConstructor()
@@ -393,16 +435,13 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
coroutineConstructor = coroutineConstructor coroutineConstructor = coroutineConstructor
) )
val invokeFunctionSymbol = val suspendInvokeFunction =
functionClass!!.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }.symbol suspendFunctionClass!!.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }
val suspendInvokeFunctionSymbol =
suspendFunctionClass!!.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }.symbol
buildInvokeMethod( buildInvokeMethod(
suspendFunctionInvokeFunctionSymbol = suspendInvokeFunctionSymbol, functionInvokeFunction = suspendInvokeFunction,
functionInvokeFunctionSymbol = invokeFunctionSymbol,
createFunction = createMethod, createFunction = createMethod,
stateMachineFunction = invokeSuspendMethod stateMachineFunction = invokeSuspendMethod,
) )
} }
@@ -549,8 +588,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
} }
private fun buildInvokeMethod( private fun buildInvokeMethod(
suspendFunctionInvokeFunctionSymbol: IrSimpleFunctionSymbol, functionInvokeFunction: IrSimpleFunction,
functionInvokeFunctionSymbol: IrSimpleFunctionSymbol,
createFunction: IrFunction, createFunction: IrFunction,
stateMachineFunction: IrFunction stateMachineFunction: IrFunction
): IrSimpleFunction = context.irFactory.buildFun { ): IrSimpleFunction = context.irFactory.buildFun {
@@ -579,8 +617,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
this.createDispatchReceiverParameter() this.createDispatchReceiverParameter()
overriddenSymbols += functionInvokeFunctionSymbol overriddenSymbols += functionInvokeFunction.symbol
overriddenSymbols += suspendFunctionInvokeFunctionSymbol
val thisReceiver = dispatchReceiverParameter!! val thisReceiver = dispatchReceiverParameter!!
@@ -251,12 +251,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
} else { } else {
val numberOfParameters = unboundFunctionParameters.size val numberOfParameters = unboundFunctionParameters.size
val functionParameterTypes = unboundFunctionParameters.map { it.type } val functionParameterTypes = unboundFunctionParameters.map { it.type }
val functionClass: IrClass val functionClass: IrClass?
val suspendFunctionClass: IrClass? val suspendFunctionClass: IrClass?
if (isKSuspendFunction) { if (isKSuspendFunction) {
functionClass = symbols.functionN(numberOfParameters + 1).owner functionClass = null
val continuationType = continuationClassSymbol.typeWith(referencedFunction.returnType)
superTypes += functionClass.typeWith(functionParameterTypes + continuationType + irBuiltIns.anyNType)
suspendFunctionClass = symbols.kSuspendFunctionN(numberOfParameters).owner suspendFunctionClass = symbols.kSuspendFunctionN(numberOfParameters).owner
superTypes += suspendFunctionClass.typeWith(functionParameterTypes + referencedFunction.returnType) superTypes += suspendFunctionClass.typeWith(functionParameterTypes + referencedFunction.returnType)
} else { } else {
@@ -275,13 +273,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
} }
} }
if (!isKSuspendFunction) if (functionClass != null)
buildInvokeMethod(functionClass.getInvokeFunction()) buildInvokeMethod(functionClass.getInvokeFunction())
if (suspendFunctionClass != null) { if (suspendFunctionClass != null) {
buildInvokeMethod(suspendFunctionClass.getInvokeFunction()).also { buildInvokeMethod(suspendFunctionClass.getInvokeFunction())
if (isKSuspendFunction)
it.overriddenSymbols += functionClass.getInvokeFunction().symbol
}
} }
} }
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrSetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSuspendableExpressionImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSuspendableExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSuspensionPointImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSuspensionPointImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
@@ -59,6 +60,20 @@ internal class NativeSuspendFunctionsLowering(ctx: Context): AbstractSuspendFunc
) )
} }
override fun IrBuilderWithScope.launchSuspendFunctionWithGivenContinuation(
symbol: IrSimpleFunctionSymbol, dispatchReceiver: IrExpression,
arguments: List<IrExpression>, continuation: IrExpression
) = irCall(this@NativeSuspendFunctionsLowering.context.ir.symbols.coroutineLaunchpad).apply {
putValueArgument(0, irCall(symbol).apply {
this.dispatchReceiver = dispatchReceiver
arguments.forEachIndexed { index, irExpression ->
putValueArgument(index, irExpression)
}
})
putValueArgument(1, continuation)
}
override fun buildStateMachine(stateMachineFunction: IrFunction, override fun buildStateMachine(stateMachineFunction: IrFunction,
transformingFunction: IrFunction, transformingFunction: IrFunction,
argumentToPropertiesMap: Map<IrValueParameter, IrField>) { argumentToPropertiesMap: Map<IrValueParameter, IrField>) {
@@ -613,7 +613,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
private fun mapReturnType(actualType: IrType, returnType: IrType) = mapWrappedType(actualType, returnType) private fun mapReturnType(actualType: IrType, returnType: IrType) = mapWrappedType(actualType, returnType)
private fun getNode(expression: IrExpression): Scoped<DataFlowIR.Node> { private fun getNode(expression: IrExpression, continuationOverride: DataFlowIR.Node? = null): Scoped<DataFlowIR.Node> {
if (expression is IrGetValue) { if (expression is IrGetValue) {
val valueDeclaration = expression.symbol.owner val valueDeclaration = expression.symbol.owner
if (valueDeclaration is IrValueParameter) if (valueDeclaration is IrValueParameter)
@@ -715,7 +715,12 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
} }
is IrCall -> when (value.symbol) { is IrCall -> when (value.symbol) {
getContinuationSymbol -> getContinuation().value getContinuationSymbol -> continuationOverride ?: getContinuation().value
symbols.coroutineLaunchpad -> getNode(
value.getValueArgument(0)!!,
continuationOverride = expressionToEdge(value.getValueArgument(1)!!).node
).value
in arrayGetSymbols -> { in arrayGetSymbols -> {
val actualCallee = value.actualCallee val actualCallee = value.actualCallee
@@ -765,7 +770,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
.map { expressionToEdge(it.second) } .map { expressionToEdge(it.second) }
.let { .let {
if (callee.isSuspend) if (callee.isSuspend)
it + DataFlowIR.Edge(getContinuation().value, null) it + DataFlowIR.Edge(continuationOverride ?: getContinuation().value, null)
else else
it it
} }
@@ -1372,21 +1372,12 @@ internal object DevirtualizationAnalysis {
callSite.origin, callSite.origin,
actualCallee.parentAsClass.symbol actualCallee.parentAsClass.symbol
) )
if (actualCallee.explicitParametersCount == arguments.size) { assert(actualCallee.explicitParametersCount == arguments.size) {
arguments.forEachIndexed { index, argument -> call.putArgument(index, argument) } "Incorrect number of arguments: expected [${actualCallee.explicitParametersCount}] but was [${arguments.size}]\n" +
return call
}
assert(actualCallee.isSuspend && actualCallee.explicitParametersCount == arguments.size - 1) {
"Incorrect number of arguments: expected [${actualCallee.explicitParametersCount}] but was [${arguments.size - 1}]\n" +
actualCallee.dump() actualCallee.dump()
} }
val continuation = arguments.last() arguments.forEachIndexed { index, argument -> call.putArgument(index, argument) }
for (index in 0..arguments.size - 2) return call
call.putArgument(index, arguments[index])
return irCall(context.ir.symbols.coroutineLaunchpad, actualType).apply {
putValueArgument(0, call)
putValueArgument(1, continuation)
}
} }
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType, fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType,
@@ -235,6 +235,7 @@ internal object FileInitializersOptimization {
} }
private val executeImplSymbol = context.ir.symbols.executeImpl private val executeImplSymbol = context.ir.symbols.executeImpl
private val coroutineLaunchpadSymbol = context.ir.symbols.coroutineLaunchpad
private val getContinuationSymbol = context.ir.symbols.getContinuation private val getContinuationSymbol = context.ir.symbols.getContinuation
private var dummySet = mutableSetOf<IrFunctionAccessExpression>() private var dummySet = mutableSetOf<IrFunctionAccessExpression>()
@@ -489,6 +490,13 @@ internal object FileInitializersOptimization {
return curData return curData
} }
private fun processCoroutineLaunchpad(expression: IrCall, data: BitSet): BitSet {
val call = expression.getValueArgument(0)!!
val continuation = expression.getValueArgument(1)!!
val curData = continuation.accept(this, data)
return call.accept(this, curData)
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BitSet) = override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BitSet) =
processCall(expression, expression.actualCallee, data) processCall(expression, expression.actualCallee, data)
@@ -499,6 +507,8 @@ internal object FileInitializersOptimization {
return processExecuteImpl(expression, data) return processExecuteImpl(expression, data)
if (expression.symbol == getContinuationSymbol) if (expression.symbol == getContinuationSymbol)
return data return data
if (expression.symbol == coroutineLaunchpadSymbol)
return processCoroutineLaunchpad(expression, data)
if (!expression.isVirtualCall) if (!expression.isVirtualCall)
return processCall(expression, expression.actualCallee, data) return processCall(expression, expression.actualCallee, data)
val devirtualizedCallSite = virtualCallSites[expression] ?: return data val devirtualizedCallSite = virtualCallSites[expression] ?: return data