Fixed bug in SuspendFunctionsLowering + test

Supported callable references to non-local suspend functions.
This commit is contained in:
Igor Chevdar
2018-06-26 17:43:09 +03:00
committed by Igor Chevdar
parent b93188a056
commit 57e75915ff
4 changed files with 111 additions and 48 deletions
@@ -146,7 +146,7 @@ internal class KonanLower(val context: Context) {
CompileTimeEvaluateLowering(context).lower(irFile) CompileTimeEvaluateLowering(context).lower(irFile)
} }
phaser.phase(KonanPhase.LOWER_COROUTINES) { phaser.phase(KonanPhase.LOWER_COROUTINES) {
SuspendFunctionsLowering(context).runOnFilePostfix(irFile) SuspendFunctionsLowering(context).lower(irFile)
} }
phaser.phase(KonanPhase.LOWER_TYPE_OPERATORS) { phaser.phase(KonanPhase.LOWER_TYPE_OPERATORS) {
TypeOperatorLowering(context).runOnFilePostfix(irFile) TypeOperatorLowering(context).runOnFilePostfix(irFile)
@@ -52,7 +52,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
internal class SuspendFunctionsLowering(val context: Context): DeclarationContainerLoweringPass { internal class SuspendFunctionsLowering(val context: Context): FileLoweringPass {
private object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL") private object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL")
private object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL") private object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL")
@@ -60,19 +60,33 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
private val builtCoroutines = mutableMapOf<FunctionDescriptor, BuiltCoroutine>() private val builtCoroutines = mutableMapOf<FunctionDescriptor, BuiltCoroutine>()
private val suspendLambdas = mutableMapOf<FunctionDescriptor, IrFunctionReference>() private val suspendLambdas = mutableMapOf<FunctionDescriptor, IrFunctionReference>()
override fun lower(irDeclarationContainer: IrDeclarationContainer) { override fun lower(irFile: IrFile) {
markSuspendLambdas(irDeclarationContainer) markSuspendLambdas(irFile)
irDeclarationContainer.declarations.transformFlat { buildCoroutines(irFile)
if (it is IrFunction && it.descriptor.isSuspend && it.descriptor.modality != Modality.ABSTRACT) transformCallableReferencesToSuspendLambdas(irFile)
transformSuspendFunction(it, suspendLambdas[it.descriptor])
else null
}
transformCallableReferencesToSuspendLambdas(irDeclarationContainer)
} }
private fun markSuspendLambdas(irDeclarationContainer: IrDeclarationContainer) { private fun buildCoroutines(irFile: IrFile) {
irDeclarationContainer.declarations.forEach { irFile.declarations.transformFlat(::tryTransformSuspendFunction)
it.acceptChildrenVoid(object: IrElementVisitorVoid { irFile.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.declarations.transformFlat(::tryTransformSuspendFunction)
}
})
}
private fun tryTransformSuspendFunction(element: IrElement) =
if (element is IrFunction && element.descriptor.isSuspend && element.descriptor.modality != Modality.ABSTRACT)
transformSuspendFunction(element, suspendLambdas[element.descriptor])
else null
private fun markSuspendLambdas(irElement: IrElement) {
irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
} }
@@ -86,11 +100,9 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
} }
}) })
} }
}
private fun transformCallableReferencesToSuspendLambdas(irDeclarationContainer: IrDeclarationContainer) { private fun transformCallableReferencesToSuspendLambdas(irElement: IrElement) {
irDeclarationContainer.declarations.forEach { irElement.transformChildrenVoid(object : IrElementTransformerVoid() {
it.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
@@ -99,7 +111,7 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
if (!descriptor.isSuspend) if (!descriptor.isSuspend)
return expression return expression
val coroutine = builtCoroutines[descriptor] val coroutine = builtCoroutines[descriptor]
?: throw Error("Non-local callable reference to suspend lambda: $descriptor") ?: throw Error("The coroutine for $descriptor has not been built")
val constructorParameters = coroutine.coroutineConstructor.valueParameters val constructorParameters = coroutine.coroutineConstructor.valueParameters
val expressionArguments = expression.getArguments().map { it.second } val expressionArguments = expression.getArguments().map { it.second }
assert(constructorParameters.size == expressionArguments.size, assert(constructorParameters.size == expressionArguments.size,
@@ -108,13 +120,13 @@ internal class SuspendFunctionsLowering(val context: Context): DeclarationContai
irBuilder.run { irBuilder.run {
return irCall(coroutine.coroutineConstructor.symbol).apply { return irCall(coroutine.coroutineConstructor.symbol).apply {
expressionArguments.forEachIndexed { index, argument -> expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument) } putValueArgument(index, argument)
}
} }
} }
} }
}) })
} }
}
private sealed class SuspendFunctionKind { private sealed class SuspendFunctionKind {
object NO_SUSPEND_CALLS : SuspendFunctionKind() object NO_SUSPEND_CALLS : SuspendFunctionKind()
+5
View File
@@ -1324,6 +1324,11 @@ task coroutines_coroutineContext2(type: RunKonanTest) {
source = "codegen/coroutines/coroutineContext2.kt" source = "codegen/coroutines/coroutineContext2.kt"
} }
task coroutines_anonymousObject(type: RunKonanTest) {
goldValue = "zzz\n"
source = "codegen/coroutines/anonymousObject.kt"
}
task AbstractMutableCollection(type: RunKonanTest) { task AbstractMutableCollection(type: RunKonanTest) {
expectedExitStatus = 0 expectedExitStatus = 0
source = "runtime/collections/AbstractMutableCollection.kt" source = "runtime/collections/AbstractMutableCollection.kt"
@@ -0,0 +1,46 @@
package codegen.coroutines.anonymousObject
import kotlin.test.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Any?> {
companion object : EmptyContinuation()
override fun resume(value: Any?) {}
override fun resumeWithException(exception: Throwable) { throw exception }
}
suspend fun suspendHere(): Int = suspendCoroutineOrReturn { x ->
x.resume(42)
COROUTINE_SUSPENDED
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
interface I {
suspend fun foo(lambda: suspend (String) -> Unit)
suspend fun bar(s: String)
}
fun create() = object: I {
var lambda: suspend (String) -> Unit = {}
override suspend fun foo(lambda: suspend (String) -> Unit) {
this.lambda = lambda
}
override suspend fun bar(s: String) {
lambda(s)
}
}
@Test fun runTest() {
builder {
val z = create()
z.foo { suspendHere(); println(it) }
z.bar("zzz")
}
}