Coroutines bug fix: correct handling of returnIfSuspended intrinsic
This commit is contained in:
+49
-30
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user