From 072da99ba36782cc25ae06e7621c82cf2bf048e4 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Thu, 18 May 2017 18:40:36 +0300 Subject: [PATCH] Coroutines bug fix: correct handling of returnIfSuspended intrinsic --- .../konan/lower/SuspendFunctionsLowering.kt | 79 ++++++++++++------- .../main/kotlin/konan/internal/Coroutines.kt | 2 +- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt index d3b9e3983ce..f446bf2228d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt @@ -206,17 +206,21 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai val lastCall = when (lastStatement) { is IrCall -> lastStatement is IrReturn -> { - var value: IrElement = lastStatement.value + var value: IrElement = lastStatement /* * Check if matches this pattern: - * block { - * block { + * block/return { + * block/return { * .. suspendCall() * } * } */ - while (value is IrBlock && value.statements.size == 1) { - value = value.statements.first() + loop@while (true) { + when { + value is IrBlock && value.statements.size == 1 -> value = value.statements.first() + value is IrReturn -> value = value.value + else -> break@loop + } } value as? IrCall } @@ -1000,28 +1004,41 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai } var calledSaveState = false - if (expression.isSuspendCall) { - val lastChild = newChildren.last() - if (lastChild != null) { - // Save state as late as possible. + var suspendCall: IrExpression? = null + when { + expression.isReturnIfSuspendedCall -> { calledSaveState = true - newChildren[numberOfChildren - 1] = - irBlock(lastChild) { - if (lastChild.isPure()) { - +irCall(saveStateSymbol) - +lastChild - } else { - val tmp = IrVariableSymbolImpl( - IrTemporaryVariableDescriptorImpl( - containingDeclaration = irFunction.descriptor, - name = "tmp${tempIndex++}".synthesizedName, - outType = lastChild.type) - ) - +irVar(tmp, lastChild) - +irCall(saveStateSymbol) - +irGet(tmp) + val firstArgument = newChildren[2]!! + newChildren[2] = irBlock(firstArgument) { + +irCall(saveStateSymbol) + +firstArgument + } + suspendCall = newChildren[2] + } + expression.isSuspendCall -> { + val lastChild = newChildren.last() + if (lastChild != null) { + // Save state as late as possible. + calledSaveState = true + newChildren[numberOfChildren - 1] = + irBlock(lastChild) { + if (lastChild.isPure()) { + +irCall(saveStateSymbol) + +lastChild + } else { + val tmp = IrVariableSymbolImpl( + IrTemporaryVariableDescriptorImpl( + containingDeclaration = irFunction.descriptor, + name = "tmp${tempIndex++}".synthesizedName, + outType = lastChild.type) + ) + +irVar(tmp, lastChild) + +irCall(saveStateSymbol) + +irGet(tmp) + } } - } + } + suspendCall = expression } } @@ -1039,12 +1056,9 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai } } - if (!expression.isSuspendCall && !expression.isReturnIfSuspendedCall) + if (suspendCall == null) return irWrap(expression, tempStatements) - val suspendCall = if (expression.isReturnIfSuspendedCall) - (expression as IrCall).getValueArgument(0)!! - else expression val suspensionPointIdParameter = IrTemporaryVariableDescriptorImpl( containingDeclaration = irFunction.descriptor, name = "suspensionPointId${suspensionPointIdIndex++}".synthesizedName, @@ -1057,7 +1071,7 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai result = irBlock(startOffset, endOffset) { if (!calledSaveState) +irCall(saveStateSymbol) - +irSetVar(suspendResult, suspendCall) + +irSetVar(suspendResult, suspendCall!!) +irReturnIfSuspended(suspendResult) +irGet(suspendResult) }, @@ -1106,6 +1120,11 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai expression.acceptChildrenVoid(this) hasSuspendCalls = hasSuspendCalls || expression.isSuspendCall } + + override fun visitExpression(expression: IrExpression) { + expression.acceptChildrenVoid(this) + hasSuspendCalls = hasSuspendCalls || expression is IrSuspensionPointImpl + } }) return hasSuspendCalls diff --git a/runtime/src/main/kotlin/konan/internal/Coroutines.kt b/runtime/src/main/kotlin/konan/internal/Coroutines.kt index f639771c33e..8a0e944a8d7 100644 --- a/runtime/src/main/kotlin/konan/internal/Coroutines.kt +++ b/runtime/src/main/kotlin/konan/internal/Coroutines.kt @@ -9,7 +9,7 @@ internal fun getContinuation(): Continuation = throw AssertionError("Call @Intrinsic @PublishedApi -internal fun returnIfSuspended(value: Any?): T = throw AssertionError("Call to returnIfSuspended should've been lowered") +internal suspend fun returnIfSuspended(value: Any?): T = throw AssertionError("Call to returnIfSuspended should've been lowered") // Single-threaded continuation. class SafeContinuation