Fixed bug in coroutines lowering

Removed unnecessary returns & blocks from suspend function with delegating tail call.
This commit is contained in:
Igor Chevdar
2018-05-15 17:11:49 +03:00
parent 7ded115ddd
commit 876de88df2
@@ -43,10 +43,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetVariableImpl
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
@@ -125,26 +122,26 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
} }
} }
private enum class SuspendFunctionKind { private sealed class SuspendFunctionKind {
NO_SUSPEND_CALLS, object NO_SUSPEND_CALLS : SuspendFunctionKind()
DELEGATING, class DELEGATING(val delegatingCall: IrCall) : SuspendFunctionKind()
NEEDS_STATE_MACHINE object NEEDS_STATE_MACHINE : SuspendFunctionKind()
} }
private fun transformSuspendFunction(irFunction: IrFunction, functionReference: IrFunctionReference?): List<IrDeclaration>? { private fun transformSuspendFunction(irFunction: IrFunction, functionReference: IrFunctionReference?): List<IrDeclaration>? {
val suspendFunctionKind = getSuspendFunctionKind(irFunction) val suspendFunctionKind = getSuspendFunctionKind(irFunction)
return when (suspendFunctionKind) { return when (suspendFunctionKind) {
SuspendFunctionKind.NO_SUSPEND_CALLS -> { is SuspendFunctionKind.NO_SUSPEND_CALLS -> {
removeReturnIfSuspendedCall(irFunction)
null // No suspend function calls - just an ordinary function. null // No suspend function calls - just an ordinary function.
} }
SuspendFunctionKind.DELEGATING -> { // Calls another suspend function at the end. is SuspendFunctionKind.DELEGATING -> { // Calls another suspend function at the end.
removeReturnIfSuspendedCall(irFunction) removeReturnIfSuspendedCallAndSimplifyDelegatingCall(
irFunction, suspendFunctionKind.delegatingCall)
null // No need in state machine. null // No need in state machine.
} }
SuspendFunctionKind.NEEDS_STATE_MACHINE -> { is SuspendFunctionKind.NEEDS_STATE_MACHINE -> {
val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation. val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation.
if (suspendLambdas.contains(irFunction.descriptor)) // Suspend lambdas are called through factory method <create>, if (suspendLambdas.contains(irFunction.descriptor)) // Suspend lambdas are called through factory method <create>,
listOf(coroutine) // thus we can eliminate original body. listOf(coroutine) // thus we can eliminate original body.
@@ -209,7 +206,7 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
return when { return when {
numberOfSuspendCalls == 0 -> SuspendFunctionKind.NO_SUSPEND_CALLS numberOfSuspendCalls == 0 -> SuspendFunctionKind.NO_SUSPEND_CALLS
numberOfSuspendCalls == 1 numberOfSuspendCalls == 1
&& suspendCallAtEnd -> SuspendFunctionKind.DELEGATING && suspendCallAtEnd -> SuspendFunctionKind.DELEGATING(lastCall!!)
else -> SuspendFunctionKind.NEEDS_STATE_MACHINE else -> SuspendFunctionKind.NEEDS_STATE_MACHINE
} }
} }
@@ -218,16 +215,17 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
private val getContinuationSymbol = symbols.getContinuation private val getContinuationSymbol = symbols.getContinuation
private val returnIfSuspendedDescriptor = context.getInternalFunctions("returnIfSuspended").single() private val returnIfSuspendedDescriptor = context.getInternalFunctions("returnIfSuspended").single()
private fun removeReturnIfSuspendedCall(irFunction: IrFunction) { private fun removeReturnIfSuspendedCallAndSimplifyDelegatingCall(irFunction: IrFunction, delegatingCall: IrCall) {
irFunction.transformChildrenVoid(object: IrElementTransformerVoid() { val returnValue =
override fun visitCall(expression: IrCall): IrExpression { if (delegatingCall.descriptor.original == returnIfSuspendedDescriptor)
expression.transformChildrenVoid(this) delegatingCall.getValueArgument(0)!!
else delegatingCall
if (expression.descriptor.original == returnIfSuspendedDescriptor) context.createIrBuilder(irFunction.symbol).run {
return expression.getValueArgument(0)!! val statements = (irFunction.body as IrBlockBody).statements
return expression val lastStatement = statements.last()
} assert (lastStatement == delegatingCall || lastStatement is IrReturn) { "Unexpected statement $lastStatement" }
}) statements[statements.size - 1] = irReturn(returnValue)
}
} }
private fun buildCoroutine(irFunction: IrFunction, functionReference: IrFunctionReference?): IrClass { private fun buildCoroutine(irFunction: IrFunction, functionReference: IrFunctionReference?): IrClass {