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) { val lastCall = when (lastStatement) {
is IrCall -> lastStatement is IrCall -> lastStatement
is IrReturn -> { is IrReturn -> {
var value: IrElement = lastStatement.value var value: IrElement = lastStatement
/* /*
* Check if matches this pattern: * Check if matches this pattern:
* block { * block/return {
* block { * block/return {
* .. suspendCall() * .. suspendCall()
* } * }
* } * }
*/ */
while (value is IrBlock && value.statements.size == 1) { loop@while (true) {
value = value.statements.first() when {
value is IrBlock && value.statements.size == 1 -> value = value.statements.first()
value is IrReturn -> value = value.value
else -> break@loop
}
} }
value as? IrCall value as? IrCall
} }
@@ -1000,28 +1004,41 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
} }
var calledSaveState = false var calledSaveState = false
if (expression.isSuspendCall) { var suspendCall: IrExpression? = null
val lastChild = newChildren.last() when {
if (lastChild != null) { expression.isReturnIfSuspendedCall -> {
// Save state as late as possible.
calledSaveState = true calledSaveState = true
newChildren[numberOfChildren - 1] = val firstArgument = newChildren[2]!!
irBlock(lastChild) { newChildren[2] = irBlock(firstArgument) {
if (lastChild.isPure()) { +irCall(saveStateSymbol)
+irCall(saveStateSymbol) +firstArgument
+lastChild }
} else { suspendCall = newChildren[2]
val tmp = IrVariableSymbolImpl( }
IrTemporaryVariableDescriptorImpl( expression.isSuspendCall -> {
containingDeclaration = irFunction.descriptor, val lastChild = newChildren.last()
name = "tmp${tempIndex++}".synthesizedName, if (lastChild != null) {
outType = lastChild.type) // Save state as late as possible.
) calledSaveState = true
+irVar(tmp, lastChild) newChildren[numberOfChildren - 1] =
+irCall(saveStateSymbol) irBlock(lastChild) {
+irGet(tmp) 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) return irWrap(expression, tempStatements)
val suspendCall = if (expression.isReturnIfSuspendedCall)
(expression as IrCall).getValueArgument(0)!!
else expression
val suspensionPointIdParameter = IrTemporaryVariableDescriptorImpl( val suspensionPointIdParameter = IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor, containingDeclaration = irFunction.descriptor,
name = "suspensionPointId${suspensionPointIdIndex++}".synthesizedName, name = "suspensionPointId${suspensionPointIdIndex++}".synthesizedName,
@@ -1057,7 +1071,7 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
result = irBlock(startOffset, endOffset) { result = irBlock(startOffset, endOffset) {
if (!calledSaveState) if (!calledSaveState)
+irCall(saveStateSymbol) +irCall(saveStateSymbol)
+irSetVar(suspendResult, suspendCall) +irSetVar(suspendResult, suspendCall!!)
+irReturnIfSuspended(suspendResult) +irReturnIfSuspended(suspendResult)
+irGet(suspendResult) +irGet(suspendResult)
}, },
@@ -1106,6 +1120,11 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
expression.acceptChildrenVoid(this) expression.acceptChildrenVoid(this)
hasSuspendCalls = hasSuspendCalls || expression.isSuspendCall hasSuspendCalls = hasSuspendCalls || expression.isSuspendCall
} }
override fun visitExpression(expression: IrExpression) {
expression.acceptChildrenVoid(this)
hasSuspendCalls = hasSuspendCalls || expression is IrSuspensionPointImpl
}
}) })
return hasSuspendCalls return hasSuspendCalls
@@ -9,7 +9,7 @@ internal fun <T> getContinuation(): Continuation<T> = throw AssertionError("Call
@Intrinsic @Intrinsic
@PublishedApi @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. // Single-threaded continuation.
class SafeContinuation<in T> class SafeContinuation<in T>