Coroutines bug fix: correct handling of returnIfSuspended intrinsic

This commit is contained in:
Igor Chevdar
2017-05-18 18:40:36 +03:00
parent e834218183
commit 072da99ba3
2 changed files with 50 additions and 31 deletions
@@ -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
@@ -9,7 +9,7 @@ internal fun <T> getContinuation(): Continuation<T> = throw AssertionError("Call
@Intrinsic
@PublishedApi
internal fun <T> returnIfSuspended(value: Any?): T = throw AssertionError("Call to returnIfSuspended should've been lowered")
internal suspend fun <T> returnIfSuspended(value: Any?): T = throw AssertionError("Call to returnIfSuspended should've been lowered")
// Single-threaded continuation.
class SafeContinuation<in T>