From bd6a0910884d0370e03812b34167a896c8eab225 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Thu, 27 Apr 2017 17:04:03 +0300 Subject: [PATCH] Coroutines lowering --- .../common/DeepCopyIrTreeWithDescriptoros.kt | 1 + .../common/descriptors/DescriptorUtils.kt | 9 +- .../common/lower/LocalDeclarationsLowering.kt | 6 +- .../kotlin/backend/common/lower/LowerUtils.kt | 13 +- .../jetbrains/kotlin/backend/konan/Context.kt | 61 +- .../kotlin/backend/konan/KonanLower.kt | 9 +- .../kotlin/backend/konan/KonanPhases.kt | 7 +- .../konan/descriptors/DescriptorUtils.kt | 4 +- .../kotlin/backend/konan/ir/IrUtils.kt | 21 +- .../kotlin/backend/konan/ir/KonanIr.kt | 53 + .../kotlin/backend/konan/lower/Autoboxing.kt | 25 +- .../lower/DefaultArgumentStubGenerator.kt | 1 + .../backend/konan/lower/FunctionInlining.kt | 8 +- .../konan/lower/SuspendFunctionsLowering.kt | 1180 +++++++++++++++++ .../main/kotlin/konan/internal/Coroutines.kt | 12 +- .../experimental/intrinsics/Intrinsics.kt | 6 +- 16 files changed, 1348 insertions(+), 68 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/DeepCopyIrTreeWithDescriptoros.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/DeepCopyIrTreeWithDescriptoros.kt index a7ea906f904..86f28a13d76 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/DeepCopyIrTreeWithDescriptoros.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/DeepCopyIrTreeWithDescriptoros.kt @@ -200,6 +200,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetDescriptor: DeclarationDe /* visibility = */ oldDescriptor.visibility ) isTailrec = oldDescriptor.isTailrec + isSuspend = oldDescriptor.isSuspend overriddenDescriptors += oldDescriptor.overriddenDescriptors } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/DescriptorUtils.kt index 1f2426173a8..89dd95e58fd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/descriptors/DescriptorUtils.kt @@ -16,10 +16,11 @@ package org.jetbrains.kotlin.backend.common.descriptors -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ParameterDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.coroutines.isSuspendLambda +import org.jetbrains.kotlin.descriptors.* + +internal val CallableDescriptor.isSuspend: Boolean + get() = this is FunctionDescriptor && isSuspend /** * @return naturally-ordered list of all parameters available inside the function body. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index a5f61d1fc72..5cd54e878ea 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* @@ -456,6 +453,7 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai oldDescriptor.source ).apply { isTailrec = oldDescriptor.isTailrec + isSuspend = oldDescriptor.isSuspend // TODO: copy other properties or consider using `FunctionDescriptor.CopyBuilder`. } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index 56bf2fb6e7f..80f74c5cce5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.konan.util.atMostOne import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.ir.IrElement @@ -138,11 +139,17 @@ fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List) : MemberScopeImpl() { - override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = TODO() + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = + members.filterIsInstance() + .atMostOne { it.name == name } - override fun getContributedVariables(name: Name, location: LookupLocation): Collection = TODO() + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = + members.filterIsInstance() + .filter { it.name == name } - override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = TODO() + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = + members.filterIsInstance() + .filter { it.name == name } override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 7227596254e..77e7697dc88 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -88,11 +88,11 @@ internal class SpecialDescriptorsFactory(val context: Context) { val bridgeDirections = overriddenFunctionDescriptor.bridgeDirections return bridgesDescriptors.getOrPut(descriptor to bridgeDirections) { SimpleFunctionDescriptorImpl.create( - descriptor.containingDeclaration, - Annotations.EMPTY, - ("" + descriptor.functionName).synthesizedName, - CallableMemberDescriptor.Kind.DECLARATION, - SourceElement.NO_SOURCE).apply { + /* containingDeclaration = */ descriptor.containingDeclaration, + /* annotations = */ Annotations.EMPTY, + /* name = */ "${descriptor.functionName}".synthesizedName, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* source = */ SourceElement.NO_SOURCE).apply { initializeBridgeDescriptor(this, descriptor, bridgeDirections.array) } } @@ -120,31 +120,34 @@ internal class SpecialDescriptorsFactory(val context: Context) { BridgeDirection.FROM_VALUE_TYPE -> context.builtIns.anyType } + val valueParameters = descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor -> + when (bridgeDirections[index + 2]) { + BridgeDirection.TO_VALUE_TYPE -> valueParameterDescriptor + BridgeDirection.NOT_NEEDED -> valueParameterDescriptor + BridgeDirection.FROM_VALUE_TYPE -> ValueParameterDescriptorImpl( + containingDeclaration = valueParameterDescriptor.containingDeclaration, + original = null, + index = index, + annotations = Annotations.EMPTY, + name = valueParameterDescriptor.name, + outType = context.builtIns.anyType, + declaresDefaultValue = valueParameterDescriptor.declaresDefaultValue(), + isCrossinline = valueParameterDescriptor.isCrossinline, + isNoinline = valueParameterDescriptor.isNoinline, + varargElementType = valueParameterDescriptor.varargElementType, + source = SourceElement.NO_SOURCE) + } + } bridgeDescriptor.initialize( - extensionReceiverType, - descriptor.dispatchReceiverParameter, - descriptor.typeParameters, - descriptor.valueParameters.mapIndexed { index, valueParameterDescriptor -> - when (bridgeDirections[index + 2]) { - BridgeDirection.TO_VALUE_TYPE -> valueParameterDescriptor - BridgeDirection.NOT_NEEDED -> valueParameterDescriptor - BridgeDirection.FROM_VALUE_TYPE -> ValueParameterDescriptorImpl( - valueParameterDescriptor.containingDeclaration, - null, - index, - Annotations.EMPTY, - valueParameterDescriptor.name, - context.builtIns.anyType, - valueParameterDescriptor.declaresDefaultValue(), - valueParameterDescriptor.isCrossinline, - valueParameterDescriptor.isNoinline, - valueParameterDescriptor.varargElementType, - SourceElement.NO_SOURCE) - } - }, - returnType, - descriptor.modality, - descriptor.visibility) + /* receiverParameterType = */ extensionReceiverType, + /* dispatchReceiverParameter = */ descriptor.dispatchReceiverParameter, + /* typeParameters = */ descriptor.typeParameters, + /* unsubstitutedValueParameters = */ valueParameters, + /* unsubstitutedReturnType = */ returnType, + /* modality = */ descriptor.modality, + /* visibility = */ descriptor.visibility).apply { + isSuspend = descriptor.isSuspend + } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index 7a5491045ef..9dbac7e558a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -62,9 +62,6 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_DELEGATION) { PropertyDelegationLowering(context).lower(irFile) } - phaser.phase(KonanPhase.LOWER_TYPE_OPERATORS) { - TypeOperatorLowering(context).runOnFilePostfix(irFile) - } phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) { LocalDeclarationsLowering(context).runOnFilePostfix(irFile) } @@ -93,6 +90,12 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_VARARG) { VarargInjectionLowering(context).runOnFilePostfix(irFile) } + phaser.phase(KonanPhase.LOWER_COROUTINES) { + SuspendFunctionsLowering(context).runOnFilePostfix(irFile) + } + phaser.phase(KonanPhase.LOWER_TYPE_OPERATORS) { + TypeOperatorLowering(context).runOnFilePostfix(irFile) + } phaser.phase(KonanPhase.BRIDGES_BUILDING) { BridgesBuilding(context).runOnFilePostfix(irFile) DirectBridgesCallsLowering(context).runOnFilePostfix(irFile) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 9cf670e1d26..1c65c802c0a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -44,10 +44,11 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering", LOWER_DEFAULT_PARAMETER_EXTENT), /* ... ... */ LOWER_LATEINIT("Lateinit properties lowering"), /* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering", LOWER_DEFAULT_PARAMETER_EXTENT, LOWER_LATEINIT), - /* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"), - /* ... ... */ BRIDGES_BUILDING("Bridges building"), + /* ... ... */ LOWER_COROUTINES("Coroutines lowering", LOWER_LOCAL_FUNCTIONS), + /* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering", LOWER_COROUTINES), + /* ... ... */ BRIDGES_BUILDING("Bridges building", LOWER_COROUTINES), /* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"), - /* ... ... */ AUTOBOX("Autoboxing of primitive types", BRIDGES_BUILDING), + /* ... ... */ AUTOBOX("Autoboxing of primitive types", BRIDGES_BUILDING, LOWER_COROUTINES), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index dce42dbdad0..7af44a421be 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -202,7 +202,7 @@ fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.moda internal fun FunctionDescriptor.hasValueTypeAt(index: Int): Boolean { when (index) { - 0 -> return returnType.let { it != null && it.isValueType() } + 0 -> return !isSuspend && returnType.let { it != null && it.isValueType() } 1 -> return extensionReceiverParameter.let { it != null && it.type.isValueType() } else -> return this.valueParameters[index - 2].type.isValueType() } @@ -210,7 +210,7 @@ internal fun FunctionDescriptor.hasValueTypeAt(index: Int): Boolean { internal fun FunctionDescriptor.hasReferenceAt(index: Int): Boolean { when (index) { - 0 -> return returnType.let { it != null && !it.isValueType() } + 0 -> return isSuspend || returnType.let { it != null && !it.isValueType() } 1 -> return extensionReceiverParameter.let { it != null && !it.type.isValueType() } else -> return !this.valueParameters[index - 2].type.isValueType() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt index f3898d121e2..352850b3d94 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt @@ -51,15 +51,7 @@ fun ir2stringWhole(ir: IrElement?, withDescriptors: Boolean = false): String { internal fun DeclarationDescriptor.createFakeOverrideDescriptor(owner: ClassDescriptor): DeclarationDescriptor? { // We need to copy descriptors for vtable building, thus take only functions and properties. return when (this) { - is FunctionDescriptor -> - newCopyBuilder() - .setOwner(owner) - .setKind(CallableMemberDescriptor.Kind.FAKE_OVERRIDE) - .setCopyOverrides(true) - .build()!!.apply { - overriddenDescriptors += this@createFakeOverrideDescriptor - } - is PropertyDescriptor -> + is CallableMemberDescriptor -> copy( /* newOwner = */ owner, /* modality = */ modality, @@ -72,6 +64,17 @@ internal fun DeclarationDescriptor.createFakeOverrideDescriptor(owner: ClassDesc } } +internal fun FunctionDescriptor.createOverriddenDescriptor(owner: ClassDescriptor, final: Boolean = true): FunctionDescriptor { + return this.newCopyBuilder() + .setOwner(owner) + .setCopyOverrides(true) + .setModality(if (final) Modality.FINAL else Modality.OPEN) + .setDispatchReceiverParameter(owner.thisAsReceiverParameter) + .build()!!.apply { + overriddenDescriptors += this@createOverriddenDescriptor + } +} + internal fun ClassDescriptor.createSimpleDelegatingConstructorDescriptor(superConstructorDescriptor: ClassConstructorDescriptor, isPrimary: Boolean = false) : ClassConstructorDescriptor { val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt index c92eb539a0c..77d28568ff9 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/KonanIr.kt @@ -18,9 +18,12 @@ package org.jetbrains.kotlin.backend.konan.ir import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.IrBlock +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrContainerExpressionBase +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBase import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType @@ -50,3 +53,53 @@ class IrInlineFunctionBody(startOffset: Int, endOffset: Int, type: KotlinType, v //-----------------------------------------------------------------------------// +interface IrSuspensionPoint : IrExpression { + var suspensionPointIdParameter: IrVariable + var result: IrExpression + var resumeResult: IrExpression +} + +interface IrSuspendableExpression : IrExpression { + var suspensionPointId: IrExpression + var result: IrExpression +} + +class IrSuspensionPointImpl(startOffset: Int, endOffset: Int, type: KotlinType, + override var suspensionPointIdParameter: IrVariable, + override var result: IrExpression, + override var resumeResult: IrExpression) + : IrExpressionBase(startOffset, endOffset, type), IrSuspensionPoint { + + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitExpression(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + suspensionPointIdParameter.accept(visitor, data) + result.accept(visitor, data) + resumeResult.accept(visitor, data) + } + + override fun transformChildren(transformer: IrElementTransformer, data: D) { + suspensionPointIdParameter = suspensionPointIdParameter.transform(transformer, data) as IrVariable + result = result.transform(transformer, data) + resumeResult = resumeResult.transform(transformer, data) + } +} + +class IrSuspendableExpressionImpl(startOffset: Int, endOffset: Int, type: KotlinType, + override var suspensionPointId: IrExpression, override var result: IrExpression) + : IrExpressionBase(startOffset, endOffset, type), IrSuspendableExpression { + + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitExpression(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + suspensionPointId.accept(visitor, data) + result.accept(visitor, data) + } + + override fun transformChildren(transformer: IrElementTransformer, data: D) { + suspensionPointId = suspensionPointId.transform(transformer, data) + result = result.transform(transformer, data) + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 42496808c23..469c116d4e2 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.AbstractValueUsageTransformer import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.isSuspend import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.ValueType import org.jetbrains.kotlin.backend.konan.descriptors.* @@ -25,7 +26,9 @@ import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs import org.jetbrains.kotlin.backend.konan.isRepresentedAs import org.jetbrains.kotlin.backend.konan.util.atMostOne import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl @@ -123,6 +126,23 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } } + private var currentFunctionDescriptor: FunctionDescriptor? = null + + override fun visitFunction(declaration: IrFunction): IrStatement { + currentFunctionDescriptor = declaration.descriptor + val result = super.visitFunction(declaration) + currentFunctionDescriptor = null + return result + } + + override fun IrExpression.useAsReturnValue(returnTarget: CallableDescriptor): IrExpression { + if (returnTarget.isSuspend && returnTarget == currentFunctionDescriptor) + return this.useAs(context.builtIns.nullableAnyType) + val returnType = returnTarget.returnType + ?: return this + return this.useAs(returnType) + } + override fun IrExpression.useAs(type: KotlinType): IrExpression { val interop = context.interopBuiltIns if (this.isNullConst() && interop.nullableInteropValueTypes.any { type.isRepresentedAs(it) }) { @@ -130,7 +150,10 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr } val actualType = when (this) { - is IrCall -> this.descriptor.original.returnType ?: this.type + is IrCall -> { + if (this.descriptor.isSuspend) context.builtIns.nullableAnyType + else this.descriptor.original.returnType ?: this.type + } is IrGetField -> this.descriptor.original.type is IrTypeOperatorCall -> when (this.operator) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultArgumentStubGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultArgumentStubGenerator.kt index e15c9d9d456..8686c059d6f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultArgumentStubGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultArgumentStubGenerator.kt @@ -410,6 +410,7 @@ private fun FunctionDescriptor.generateDefaultsDescription(context: Context): Fu /* unsubstitutedReturnType = */ returnType, /* modality = */ Modality.FINAL, /* visibility = */ this.visibility) + descriptor.isSuspend = this.isSuspend context.log{"adds to cache[$this] = $descriptor"} descriptor } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index a3d0bf4975a..34e6064947d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -27,17 +27,13 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isFunctionInvoke import org.jetbrains.kotlin.backend.konan.descriptors.needsInlining import org.jetbrains.kotlin.backend.konan.ir.DeserializerDriver import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ValueDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrMemberAccessExpressionBase -import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue 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 new file mode 100644 index 00000000000..ecd381b2c9c --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/SuspendFunctionsLowering.kt @@ -0,0 +1,1180 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters +import org.jetbrains.kotlin.backend.common.descriptors.isSuspend +import org.jetbrains.kotlin.backend.common.lower.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.* +import org.jetbrains.kotlin.backend.konan.ir.* +import org.jetbrains.kotlin.backend.konan.util.atMostOne +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.getArguments +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable + +internal class SuspendFunctionsLowering(val context: Context): DeclarationContainerLoweringPass { + + private val builtCoroutines = mutableMapOf() + private val suspendLambdas = mutableMapOf() + + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + markSuspendLambdas(irDeclarationContainer) + irDeclarationContainer.declarations.transformFlat { + if (it is IrFunction && it.descriptor.isSuspend && it.descriptor.modality != Modality.ABSTRACT) + transformSuspendFunction(it, suspendLambdas[it.descriptor]) + else null + } + transformCallableReferencesToSuspendLambdas(irDeclarationContainer) + } + + private fun markSuspendLambdas(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.forEach { + it.acceptChildrenVoid(object: IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitCallableReference(expression: IrCallableReference) { + expression.acceptChildrenVoid(this) + + val descriptor = expression.descriptor + if (descriptor.isSuspend) + suspendLambdas.put(descriptor as FunctionDescriptor, expression) + } + }) + } + } + + private fun transformCallableReferencesToSuspendLambdas(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.forEach { + it.transformChildrenVoid(object: IrElementTransformerVoid() { + + override fun visitCallableReference(expression: IrCallableReference): IrExpression { + expression.transformChildrenVoid(this) + + val descriptor = expression.descriptor + if (!descriptor.isSuspend) return expression + val coroutine = builtCoroutines[descriptor] + ?: throw Error("Non-local callable reference to suspend lambda: $descriptor") + val constructorParameters = coroutine.coroutineConstructorDescriptor.valueParameters + val expressionArguments = expression.getArguments().map { it.second } + assert (constructorParameters.size == expressionArguments.size, + { "Inconsistency between callable reference to suspend lambda and the corresponding coroutine" }) + val irBuilder = context.createIrBuilder(descriptor, expression.startOffset, expression.endOffset) + irBuilder.run { + return irCall(coroutine.coroutineConstructorDescriptor).apply { + expressionArguments.forEachIndexed { index, argument -> + putValueArgument(index, argument) } + } + } + } + }) + } + } + + private enum class SuspendFunctionKind { + NO_SUSPEND_CALLS, + DELEGATING, + NEEDS_STATE_MACHINE + } + + private fun transformSuspendFunction(irFunction: IrFunction, callableReference: IrCallableReference?): List? { + val suspendFunctionKind = getSuspendFunctionKind(irFunction) + return when (suspendFunctionKind) { + SuspendFunctionKind.NO_SUSPEND_CALLS -> { + removeReturnIfSuspendedCall(irFunction) + null // No suspend function calls - just an ordinary function. + } + + SuspendFunctionKind.DELEGATING -> { // Calls another suspend function at the end. + removeReturnIfSuspendedCall(irFunction) + null // No need in state machine. + } + + SuspendFunctionKind.NEEDS_STATE_MACHINE -> { + val coroutine = buildCoroutine(irFunction, callableReference) // Coroutine implementation. + if (suspendLambdas.contains(irFunction.descriptor)) // Suspend lambdas are called through factory method , + listOf(coroutine) // thus we can eliminate original body. + else + listOf( + coroutine, + irFunction + ) + } + } + } + + private fun getSuspendFunctionKind(irFunction: IrFunction): SuspendFunctionKind { + if (suspendLambdas.contains(irFunction.descriptor)) + return SuspendFunctionKind.NEEDS_STATE_MACHINE // Suspend lambdas always need coroutine implementation. + val body = irFunction.body!! + var numberOfSuspendCalls = 0 + body.acceptVoid(object: IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitCall(expression: IrCall) { + expression.acceptChildrenVoid(this) + + if (expression.descriptor.isSuspend) + ++numberOfSuspendCalls + } + }) + // It is important to optimize the case where there is only one suspend call and it is the last statement + // because we don't need to build a fat coroutine class in that case. + // This happens a lot in practise because of suspend functions with default arguments. + val lastStatement = (body as IrBlockBody).statements.lastOrNull() + val lastCall = when (lastStatement) { + is IrCall -> lastStatement + is IrReturn -> { + var value: IrElement = lastStatement.value + /* + * Check if matches this pattern: + * block { + * block { + * .. suspendCall() + * } + * } + */ + while (value is IrBlock && value.statements.size == 1) { + value = value.statements.first() + } + value as? IrCall + } + else -> null + } + val suspendCallAtEnd = lastCall != null && lastCall.descriptor.isSuspend // Suspend call. + return when { + numberOfSuspendCalls == 0 -> SuspendFunctionKind.NO_SUSPEND_CALLS + numberOfSuspendCalls == 1 + && suspendCallAtEnd -> SuspendFunctionKind.DELEGATING + else -> SuspendFunctionKind.NEEDS_STATE_MACHINE + } + } + + private val getContinuationDescriptor = context.builtIns.getKonanInternalFunctions("getContinuation").single() + private val returnIfSuspendedDescriptor = context.builtIns.getKonanInternalFunctions("returnIfSuspended").single() + + private fun FunctionDescriptor.substitute(vararg types: KotlinType): FunctionDescriptor { + val typeSubstitutor = TypeSubstitutor.create( + typeParameters + .withIndex() + .associateBy({ it.value.typeConstructor }, { TypeProjectionImpl(types[it.index]) }) + ) + return substitute(typeSubstitutor)!! + } + + private fun removeReturnIfSuspendedCall(irFunction: IrFunction) { + irFunction.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + + if (expression.descriptor.original == returnIfSuspendedDescriptor) + return expression.getValueArgument(0)!! + return expression + } + }) + } + + private fun buildCoroutine(irFunction: IrFunction, callableReference: IrCallableReference?): IrClass { + val descriptor = irFunction.descriptor + val coroutine = CoroutineBuilder(irFunction, callableReference).build() + builtCoroutines.put(descriptor, coroutine) + + if (callableReference == null) { + // It is not a lambda - replace original function with a call to constructor of the built coroutine. + val irBuilder = context.createIrBuilder(descriptor, irFunction.startOffset, irFunction.endOffset) + irFunction.body = irBuilder.irBlockBody(irFunction) { + +irReturn( + irCall(coroutine.doResumeFunctionDescriptor).apply { + dispatchReceiver = irCall(coroutine.coroutineConstructorDescriptor).apply { + val functionParameters = irFunction.descriptor.explicitParameters + functionParameters.forEachIndexed { index, argument -> + putValueArgument(index, irGet(argument)) + } + putValueArgument(functionParameters.size, + irCall(getContinuationDescriptor.substitute(descriptor.returnType!!))) + } + putValueArgument(0, irUnit()) // value + putValueArgument(1, irNull()) // exception + }) + } + } + + return coroutine.coroutineClass + } + + private abstract class DescriptorWithIrBuilder { + + protected abstract fun buildDescriptor(): D + + protected open fun doInitialize() { } + + protected abstract fun buildIr(): B + + val descriptor by lazy { buildDescriptor() } + + private val builtIr by lazy { buildIr() } + private var initialized: Boolean = false + + fun initialize() { + doInitialize() + initialized = true + } + + val ir: B + get() { + if (!initialized) + throw Error("Access to IR before initialization") + return builtIr + } + } + + private class BuiltCoroutine(val coroutineClass: IrClass, + val coroutineConstructorDescriptor: ClassConstructorDescriptor, + val doResumeFunctionDescriptor: FunctionDescriptor) + + private var coroutineId = 0 + + private inner class CoroutineBuilder(val irFunction: IrFunction, val callableReference: IrCallableReference?) { + + private var tempIndex = 0 + private var suspensionPointIdIndex = 0 + private lateinit var suspendResult: VariableDescriptor + private lateinit var dataArgument: ValueParameterDescriptor + private lateinit var exceptionArgument: ValueParameterDescriptor + private lateinit var coroutineClassDescriptor: ClassDescriptorImpl + private lateinit var argumentToPropertiesMap: Map + private val coroutineMembers = mutableListOf() + + private val COROUTINES_INTRINSICS_FQ_NAME = FqName.fromSegments(listOf("kotlin", "coroutines", "experimental", "intrinsics")) + private val coroutinesIntrinsicsScope = context.irModule!!.descriptor.getPackage(COROUTINES_INTRINSICS_FQ_NAME).memberScope + + private val coroutineImplClassDescriptor = context.builtIns.getKonanInternalClass("CoroutineImpl") + private val create1FunctionDescriptor = coroutineImplClassDescriptor.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("create"), NoLookupLocation.FROM_BACKEND) + .single { it.valueParameters.size == 1 } + private val create1CompletionParameter = create1FunctionDescriptor.valueParameters[0] + + private val COROUTINE_SUSPENDED = coroutinesIntrinsicsScope + .getContributedVariables(Name.identifier("COROUTINE_SUSPENDED"), NoLookupLocation.FROM_BACKEND).first() + + fun build(): BuiltCoroutine { + coroutineClassDescriptor = ClassDescriptorImpl( + /* containingDeclaration = */ irFunction.descriptor.containingDeclaration, + /* name = */ "${irFunction.descriptor.name}\$${coroutineId++}".synthesizedName, + /* modality = */ Modality.FINAL, + /* kind = */ ClassKind.CLASS, + /* superTypes = */ listOf(coroutineImplClassDescriptor.defaultType), + /* source = */ SourceElement.NO_SOURCE, + /* isExternal = */ false + ) + val overriddenMap = mutableMapOf() + val constructors = mutableSetOf() + val coroutineConstructorBuilder = createConstructorBuilder() + constructors.add(coroutineConstructorBuilder.descriptor) + + var coroutineFactoryConstructorBuilder: DescriptorWithIrBuilder? = null + var createMethodBuilder: DescriptorWithIrBuilder? = null + if (callableReference != null) { + // Suspend lambda - create factory methods. + val boundArgs = callableReference.getArguments() + val boundParams = boundArgs.map { it.first } + + val allParams = irFunction.descriptor.explicitParameters + val unboundParams = allParams - boundParams + + coroutineFactoryConstructorBuilder = createFactoryConstructorBuilder(boundParams) + constructors.add(coroutineFactoryConstructorBuilder.descriptor) + val createFunctionDescriptor = coroutineImplClassDescriptor.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("create"), NoLookupLocation.FROM_BACKEND) + .atMostOne { it.valueParameters.size == unboundParams.size + 1 } + createMethodBuilder = createCreateMethodBuilder(unboundParams, createFunctionDescriptor, coroutineConstructorBuilder.descriptor) + + if (createFunctionDescriptor != null) + overriddenMap += createFunctionDescriptor to createMethodBuilder.descriptor + } + + val doResumeFunctionDescriptor = coroutineImplClassDescriptor.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_BACKEND).single() + + val doResumeMethodBuilder = createDoResumeMethodBuilder(doResumeFunctionDescriptor) + + overriddenMap += doResumeFunctionDescriptor to doResumeMethodBuilder.descriptor + + val contributedDescriptors = coroutineImplClassDescriptor.unsubstitutedMemberScope + .getContributedDescriptors() + .map { overriddenMap[it] ?: it.createFakeOverrideDescriptor(coroutineClassDescriptor) } + .filterNotNull() + .toList() + coroutineClassDescriptor.initialize(SimpleMemberScope(contributedDescriptors), constructors, null) + + coroutineConstructorBuilder.initialize() + coroutineMembers.add(coroutineConstructorBuilder.ir) + + coroutineFactoryConstructorBuilder?.let { + it.initialize() + coroutineMembers.add(it.ir) + } + + createMethodBuilder?.let { + it.initialize() + coroutineMembers.add(it.ir) + } + + doResumeMethodBuilder.initialize() + coroutineMembers.add(doResumeMethodBuilder.ir) + + val coroutineClass = IrClassImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = coroutineClassDescriptor, + members = coroutineMembers + ) + return BuiltCoroutine( + coroutineClass = coroutineClass, + coroutineConstructorDescriptor = coroutineFactoryConstructorBuilder?.descriptor + ?: coroutineConstructorBuilder.descriptor, + doResumeFunctionDescriptor = doResumeMethodBuilder.descriptor) + } + + private fun createConstructorBuilder() + = object : DescriptorWithIrBuilder() { + + private val coroutineImplConstructorDescriptor = coroutineImplClassDescriptor.constructors.single() + private lateinit var constructorParameters: List + private lateinit var functionParameters: List + + override fun buildDescriptor(): ClassConstructorDescriptorImpl { + return ClassConstructorDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* isPrimary = */ false, + /* source = */ SourceElement.NO_SOURCE + ) + } + + override fun doInitialize() { + val functionDescriptor = irFunction.descriptor + functionParameters = functionDescriptor.explicitParameters + constructorParameters = ( + functionParameters + + coroutineImplConstructorDescriptor.valueParameters[0] // completion. + ).mapIndexed { index, parameter -> parameter.copyAsValueParameter(descriptor, index) } + + descriptor.initialize(constructorParameters, Visibilities.PUBLIC) + descriptor.returnType = coroutineClassDescriptor.defaultType + } + + override fun buildIr(): IrConstructor { + // Save all arguments to fields. + argumentToPropertiesMap = functionParameters.associate { + it to buildPropertyWithBackingField(it.name, it.type, false) + } + + val startOffset = irFunction.startOffset + val endOffset = irFunction.endOffset + val irBuilder = context.createIrBuilder(descriptor, startOffset, endOffset) + return IrConstructorImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + body = irBuilder.irBlockBody { + val completionParameter = descriptor.valueParameters.last() + +IrDelegatingConstructorCallImpl(startOffset, endOffset, coroutineImplConstructorDescriptor).apply { + putValueArgument(0, irGet(completionParameter)) + } + +IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClassDescriptor) + functionParameters.forEachIndexed { index, parameter -> + +irSetField(irThis(), argumentToPropertiesMap[parameter]!!, irGet(constructorParameters[index])) + } + } + } + } + } + + private fun ParameterDescriptor.copyAsValueParameter(newOwner: CallableDescriptor, index: Int) + = when (this) { + is ValueParameterDescriptor -> this.copy(newOwner, name, index) + is ReceiverParameterDescriptor -> ValueParameterDescriptorImpl( + containingDeclaration = newOwner, + original = null, + index = index, + annotations = annotations, + name = name, + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = source + ) + else -> throw Error("Unexpected parameter descriptor: $this") + } + + private fun createFactoryConstructorBuilder(boundParams: List) + = object : DescriptorWithIrBuilder() { + + private val coroutineImplConstructorDescriptor = coroutineImplClassDescriptor.constructors.single() + private lateinit var constructorParameters: List + + override fun buildDescriptor(): ClassConstructorDescriptorImpl { + return ClassConstructorDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* isPrimary = */ false, + /* source = */ SourceElement.NO_SOURCE + ) + } + + override fun doInitialize() { + constructorParameters = boundParams.mapIndexed { index, parameter -> + parameter.copyAsValueParameter(descriptor, index) + } + descriptor.initialize(constructorParameters, Visibilities.PUBLIC) + descriptor.returnType = coroutineClassDescriptor.defaultType + } + + override fun buildIr(): IrConstructor { + val startOffset = irFunction.startOffset + val endOffset = irFunction.endOffset + val irBuilder = context.createIrBuilder(descriptor, startOffset, endOffset) + return IrConstructorImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + body = irBuilder.irBlockBody { + +IrDelegatingConstructorCallImpl(startOffset, endOffset, coroutineImplConstructorDescriptor).apply { + putValueArgument(0, irNull()) // Completion. + } + +IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClassDescriptor) + // Save all arguments to fields. + boundParams.forEachIndexed { index, parameter -> + +irSetField(irThis(), argumentToPropertiesMap[parameter]!!, irGet(constructorParameters[index])) + } + } + } + } + } + + private fun createCreateMethodBuilder(unboundArgs: List, + superFunctionDescriptor: FunctionDescriptor?, + coroutineConstructorDescriptor: ClassConstructorDescriptor) + = object: DescriptorWithIrBuilder() { + + override fun buildDescriptor() = SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* name = */ Name.identifier("create"), + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* source = */ SourceElement.NO_SOURCE) + + override fun doInitialize() { + val valueParameters = ( + unboundArgs + create1CompletionParameter + ).mapIndexed { index, parameter -> + parameter.copyAsValueParameter(descriptor, index) + } + + descriptor.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameter = */ coroutineClassDescriptor.thisAsReceiverParameter, + /* typeParameters = */ emptyList(), + /* unsubstitutedValueParameters = */ valueParameters, + /* unsubstitutedReturnType = */ coroutineClassDescriptor.defaultType, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE).apply { + if (superFunctionDescriptor != null) { + overriddenDescriptors += superFunctionDescriptor.overriddenDescriptors + overriddenDescriptors += superFunctionDescriptor + } + } + } + + override fun buildIr(): IrFunction { + val startOffset = irFunction.startOffset + val endOffset = irFunction.endOffset + val ourDescriptor = descriptor + val irBuilder = context.createIrBuilder(descriptor, startOffset, endOffset) + return IrFunctionImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + body = irBuilder.irBlockBody(startOffset, endOffset) { + +irReturn( + irCall(coroutineConstructorDescriptor).apply { + val functionParameters = irFunction.descriptor.explicitParameters + var unboundIndex = 0 + val unboundArgsSet = unboundArgs.toSet() + functionParameters.map { + if (unboundArgsSet.contains(it)) + irGet(ourDescriptor.valueParameters[unboundIndex++]) + else + irGet(irThis(), argumentToPropertiesMap[it]!!) + }.forEachIndexed { index, argument -> + putValueArgument(index, argument) + } + putValueArgument(functionParameters.size, irGet(ourDescriptor.valueParameters[unboundIndex])) + assert(unboundIndex == ourDescriptor.valueParameters.size - 1, + { "Not all arguments of are used" }) + }) + } + } + } + } + + private fun createPropertyGetterBuilder(propertyDescriptor: PropertyDescriptor, type: KotlinType) + = object: DescriptorWithIrBuilder() { + + override fun buildDescriptor() = PropertyGetterDescriptorImpl( + /* correspondingProperty = */ propertyDescriptor, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isDefault = */ false, + /* isExternal = */ false, + /* isInline = */ false, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* original = */ null, + /* source = */ SourceElement.NO_SOURCE + ) + + override fun doInitialize() { + descriptor.apply { + initialize(type) + } + } + + override fun buildIr() = IrFunctionImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + body = context.createIrBuilder(descriptor, startOffset, endOffset).irBlockBody { + +irReturn(irGetField(irThis(), propertyDescriptor)) + } + } + } + + private fun createPropertySetterBuilder(propertyDescriptor: PropertyDescriptor, type: KotlinType) + = object: DescriptorWithIrBuilder() { + + override fun buildDescriptor() = PropertySetterDescriptorImpl( + /* correspondingProperty = */ propertyDescriptor, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isDefault = */ false, + /* isExternal = */ false, + /* isInline = */ false, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* original = */ null, + /* source = */ SourceElement.NO_SOURCE + ) + + lateinit var valueParameterDescriptor: ValueParameterDescriptor + + override fun doInitialize() { + descriptor.apply { + valueParameterDescriptor = ValueParameterDescriptorImpl( + containingDeclaration = this, + original = null, + index = 0, + annotations = Annotations.EMPTY, + name = Name.identifier("value"), + outType = type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE + ) + + initialize(valueParameterDescriptor) + } + } + + override fun buildIr() = IrFunctionImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + body = context.createIrBuilder(descriptor, startOffset, endOffset).irBlockBody { + +irSetField(irThis(), propertyDescriptor, irGet(valueParameterDescriptor)) + } + } + } + + private fun createPropertyWithBackingFieldBuilder(name: Name, type: KotlinType, isMutable: Boolean) + = object: DescriptorWithIrBuilder() { + + private lateinit var getterBuilder: DescriptorWithIrBuilder + private var setterBuilder: DescriptorWithIrBuilder? = null + + override fun buildDescriptor() = PropertyDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE, + /* isVar = */ isMutable, + /* name = */ name, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* source = */ SourceElement.NO_SOURCE, + /* lateInit = */ false, + /* isConst = */ false, + /* isHeader = */ false, + /* isImpl = */ false, + /* isExternal = */ false, + /* isDelegated = */ false) + + override fun doInitialize() { + getterBuilder = createPropertyGetterBuilder(descriptor, type).apply { initialize() } + if (isMutable) + setterBuilder = createPropertySetterBuilder(descriptor, type).apply { initialize() } + descriptor.initialize(getterBuilder.descriptor, setterBuilder?.descriptor) + val receiverType: KotlinType? = null + descriptor.setType(type, emptyList(), coroutineClassDescriptor.thisAsReceiverParameter, receiverType) + } + + override fun buildIr(): IrProperty { + val startOffset = irFunction.startOffset + val endOffset = irFunction.endOffset + val backingField = IrFieldImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor) + return IrPropertyImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + isDelegated = false, + descriptor = descriptor, + backingField = backingField, + getter = getterBuilder.ir, + setter = setterBuilder?.ir) + } + } + + private fun buildPropertyWithBackingField(name: Name, type: KotlinType, isMutable: Boolean): PropertyDescriptor { + val propertyBuilder = createPropertyWithBackingFieldBuilder(name, type, isMutable).apply { initialize() } + + coroutineMembers.add(propertyBuilder.ir) + return propertyBuilder.descriptor + } + + private fun createDoResumeMethodBuilder(doResumeFunctionDescriptor: FunctionDescriptor) + = object: DescriptorWithIrBuilder() { + + override fun buildDescriptor() = doResumeFunctionDescriptor.createOverriddenDescriptor(coroutineClassDescriptor) + + override fun doInitialize() { } + + override fun buildIr(): IrFunction { + dataArgument = descriptor.valueParameters[0] + exceptionArgument = descriptor.valueParameters[1] + suspendResult = IrTemporaryVariableDescriptorImpl( + containingDeclaration = irFunction.descriptor, + name = "suspendResult".synthesizedName, + outType = context.builtIns.nullableAnyType, + isMutable = true) + val label = coroutineClassDescriptor.unsubstitutedMemberScope + .getContributedVariables(Name.identifier("label"), NoLookupLocation.FROM_BACKEND).single() + + val originalBody = irFunction.body!! + val startOffset = irFunction.startOffset + val endOffset = irFunction.endOffset + val irBuilder = context.createIrBuilder(descriptor, startOffset, endOffset) + return IrFunctionImpl( + startOffset = startOffset, + endOffset = endOffset, + origin = DECLARATION_ORIGIN_COROUTINE_IMPL, + descriptor = descriptor).apply { + + body = irBuilder.irBlockBody(startOffset, endOffset) { + + // Extract all suspend calls to temporaries in order to make correct jumps to them. + originalBody.transformChildrenVoid(ExpressionSlicer(label.type)) + + val liveLocals = computeLivenessAtSuspensionPoints(originalBody) + + val immutableLiveLocals = liveLocals.values.flatten().filterNot { it.isVar }.toSet() + val localsMap = immutableLiveLocals.associate { + it to IrTemporaryVariableDescriptorImpl( + containingDeclaration = irFunction.descriptor, + name = it.name, + outType = it.type, + isMutable = true) + } + + if (localsMap.isNotEmpty()) + transformVariables(originalBody, localsMap) // Make variables mutable in order to save/restore them. + + val localToPropertyMap = mutableMapOf() + // TODO: optimize by using the same property for different locals. + liveLocals.values.forEach { scope -> + scope.forEach { + localToPropertyMap.getOrPut(it) { + buildPropertyWithBackingField(it.name, it.type, true) + } + } + } + + originalBody.transformChildrenVoid(object : IrElementTransformerVoid() { + + // Replace returns to refer to the new function. + override fun visitReturn(expression: IrReturn): IrExpression { + expression.transformChildrenVoid(this) + + return if (expression.returnTarget != irFunction.descriptor) + expression + else + irReturn(expression.value) + } + + // Replace function arguments loading with properties reading. + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + + val capturedValue = argumentToPropertiesMap[expression.descriptor] + ?: return expression + return irGet(irThis(), capturedValue) + } + + // Save/restore state at suspension points. + override fun visitExpression(expression: IrExpression): IrExpression { + expression.transformChildrenVoid(this) + + val suspensionPoint = expression as? IrSuspensionPoint + ?: return expression + + suspensionPoint.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + val descriptor = expression.descriptor + when (descriptor) { + saveStateDescriptor -> { + val scope = liveLocals[suspensionPoint]!! + return irBlock(expression) { + scope.forEach { + +irSet(irThis(), localToPropertyMap[it]!!, irGet(localsMap[it] ?: it)) + } + +irSet(irThis(), label, irGet(suspensionPoint.suspensionPointIdParameter.descriptor)) + } + } + restoreStateDescriptor -> { + val scope = liveLocals[suspensionPoint]!! + return irBlock(expression) { + scope.forEach { + +irSetVar(localsMap[it] ?: it, irGet(irThis(), localToPropertyMap[it]!!)) + } + } + } + } + return expression + } + }) + + return suspensionPoint + } + }) + val statements = (originalBody as IrBlockBody).statements + +irVar(suspendResult, null) + +IrSuspendableExpressionImpl( + startOffset = startOffset, + endOffset = endOffset, + type = context.builtIns.unitType, + suspensionPointId = irGet(irThis(), label), + result = irBlock(startOffset, endOffset) { + +irThrowIfNotNull(exceptionArgument) // Coroutine might start with an exception. + statements.forEach { +it } + }) + } + } + } + } + + private fun transformVariables(element: IrElement, variablesMap: Map) { + element.transformChildrenVoid(object: IrElementTransformerVoid() { + + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + + val newVariable = variablesMap[expression.descriptor] + ?: return expression + + return IrGetValueImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + descriptor = newVariable, + origin = expression.origin) + } + + override fun visitSetVariable(expression: IrSetVariable): IrExpression { + expression.transformChildrenVoid(this) + + val newVariable = variablesMap[expression.descriptor] + ?: return expression + + return IrSetVariableImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + descriptor = newVariable, + value = expression.value, + origin = expression.origin) + } + + override fun visitVariable(declaration: IrVariable): IrStatement { + declaration.transformChildrenVoid(this) + + val newVariable = variablesMap[declaration.descriptor] + ?: return declaration + + return IrVariableImpl( + startOffset = declaration.startOffset, + endOffset = declaration.endOffset, + origin = declaration.origin, + descriptor = newVariable, + initializer = declaration.initializer) + } + + override fun visitCatch(aCatch: IrCatch): IrCatch { + aCatch.transformChildrenVoid(this) + + val newVariable = variablesMap[aCatch.parameter] + ?: return aCatch + + return IrCatchImpl( + startOffset = aCatch.startOffset, + endOffset = aCatch.endOffset, + parameter = newVariable, + result = aCatch.result + ) + } + }) + } + + private fun computeLivenessAtSuspensionPoints(body: IrBody): Map> { + // TODO: data flow analysis. + // Just save all visible for now. + val result = mutableMapOf>() + body.acceptChildrenVoid(object: VariablesScopeTracker() { + + override fun visitExpression(expression: IrExpression) { + val suspensionPoint = expression as? IrSuspensionPoint + if (suspensionPoint == null) { + super.visitExpression(expression) + return + } + + suspensionPoint.result.acceptChildrenVoid(this) + suspensionPoint.resumeResult.acceptChildrenVoid(this) + + val visibleVariables = mutableListOf() + scopeStack.forEach { visibleVariables += it } + result.put(suspensionPoint, visibleVariables) + } + }) + + return result + } + + // These are marker descriptors to split up the lowering on two parts. + private val saveStateDescriptor = SimpleFunctionDescriptorImpl.create( + irFunction.descriptor, + Annotations.EMPTY, + "saveState".synthesizedName, + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE).apply { + initialize(null, null, emptyList(), emptyList(), context.builtIns.unitType, Modality.ABSTRACT, Visibilities.PRIVATE) + } + + private val restoreStateDescriptor = SimpleFunctionDescriptorImpl.create( + irFunction.descriptor, + Annotations.EMPTY, + "restoreState".synthesizedName, + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE).apply { + initialize(null, null, emptyList(), emptyList(), context.builtIns.unitType, Modality.ABSTRACT, Visibilities.PRIVATE) + } + + private inner class ExpressionSlicer(val suspensionPointIdType: KotlinType): IrElementTransformerVoid() { + // TODO: optimize - it has square complexity. + + override fun visitSetField(expression: IrSetField): IrExpression { + expression.transformChildrenVoid(this) + + return sliceExpression(expression) + } + + override fun visitMemberAccess(expression: IrMemberAccessExpression): IrExpression { + expression.transformChildrenVoid(this) + + return sliceExpression(expression) + } + + private fun sliceExpression(expression: IrExpression): IrExpression { + val irBuilder = context.createIrBuilder(irFunction.descriptor, expression.startOffset, expression.endOffset) + irBuilder.run { + val children = when (expression) { + is IrSetField -> listOf(expression.receiver, expression.value) + is IrMemberAccessExpression -> ( + listOf(expression.dispatchReceiver, expression.extensionReceiver) + + expression.descriptor.valueParameters.map { expression.getValueArgument(it.index) } + ) + else -> throw Error("Unexpected expression: $expression") + } + + val numberOfChildren = children.size + + val hasSuspendCallInTail = BooleanArray(numberOfChildren + 1) + for (i in numberOfChildren - 1 downTo 0) + hasSuspendCallInTail[i] = hasSuspendCallInTail[i + 1] || children[i].let { it != null && it.hasSuspendCalls() } + + val newChildren = arrayOfNulls(numberOfChildren) + val tempStatements = mutableListOf() + for ((index, child) in children.withIndex()) { + if (child == null) continue + val transformedChild = + if (!child.isSpecialBlock()) + child + else { + val statements = (child as IrBlock).statements + tempStatements += statements.take(statements.size - 1) + statements.last() as IrExpression + } + if ((!transformedChild.isPure() && hasSuspendCallInTail[index]) + || (expression.isSuspendCall && transformedChild.hasExternalVarSets())) { + // Save to temporary in order to save execution order. + val tmp = IrTemporaryVariableDescriptorImpl( + containingDeclaration = irFunction.descriptor, + name = "tmp${tempIndex++}".synthesizedName, + outType = transformedChild.type) + tempStatements += irVar(tmp, transformedChild) + newChildren[index] = irGet(tmp) + } + else newChildren[index] = transformedChild + } + + when (expression) { + is IrSetField -> { + expression.receiver = newChildren[0] + expression.value = newChildren[1]!! + } + is IrMemberAccessExpression -> { + expression.dispatchReceiver = newChildren[0] + expression.extensionReceiver = newChildren[1] + newChildren.drop(2).forEachIndexed { index, newChild -> + expression.putValueArgument(index, newChild) + } + } + } + + if (!expression.isSuspendCall && !expression.isReturnIfSuspendedCall) + 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, + outType = suspensionPointIdType) + val currentSuspendResult = IrTemporaryVariableDescriptorImpl( + containingDeclaration = irFunction.descriptor, + name = "currentSuspendResult${tempIndex++}".synthesizedName, + outType = context.builtIns.nullableAnyType) + val suspensionPoint = IrSuspensionPointImpl( + startOffset = startOffset, + endOffset = endOffset, + type = context.builtIns.nullableAnyType, + suspensionPointIdParameter = irVar(suspensionPointIdParameter, null), + result = irBlock(startOffset, endOffset) { + +irCall(saveStateDescriptor) + +irSetVar(suspendResult, suspendCall) + +irReturnIfSuspended(suspendResult) + +irGet(suspendResult) + }, + resumeResult = irBlock(startOffset, endOffset) { + +irCall(restoreStateDescriptor) + +irThrowIfNotNull(exceptionArgument) + +irGet(dataArgument) + }) + tempStatements.add(irVar(currentSuspendResult, suspensionPoint)) + return irWrap(irCast(irGet(currentSuspendResult), suspendCall.type, suspendCall.type), tempStatements) + } + + } + + private fun IrBuilderWithScope.irWrap(expression: IrExpression, tempStatements: List) + = if (tempStatements.isEmpty()) + expression + else irBlock(expression, STATEMENT_ORIGIN_COROUTINE_IMPL) { + tempStatements.forEach { +it } + +expression + } + + private val IrExpression.isSuspendCall: Boolean + get() = this is IrCall && this.descriptor.isSuspend + + private object STATEMENT_ORIGIN_COROUTINE_IMPL: IrStatementOriginImpl("COROUTINE_IMPL") + + private fun IrElement.isSpecialBlock() = this is IrBlock && this.origin == STATEMENT_ORIGIN_COROUTINE_IMPL + + private fun IrElement.hasSuspendCalls(): Boolean { + var hasSuspendCalls = false + acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitCall(expression: IrCall) { + hasSuspendCalls = hasSuspendCalls || expression.isSuspendCall + } + }) + + return hasSuspendCalls + } + + private fun IrExpression.isPure(): Boolean { + return when (this) { + is IrConst<*> -> true + is IrCall -> false // TODO: skip builtin operators. + is IrTypeOperatorCall -> this.argument.isPure() + is IrGetValue -> !this.descriptor.let { it is VariableDescriptor && it.isVar } + else -> false + } + } + + private fun IrElement.hasExternalVarSets(): Boolean { + var hasExternalVarSets = false + acceptVoid(object: VariablesScopeTracker() { + + override fun visitSetVariable(expression: IrSetVariable) { + super.visitSetVariable(expression) + if (scopeStack.all { !it.contains(expression.descriptor) }) + hasExternalVarSets = true + } + }) + + return hasExternalVarSets + } + + private val IrExpression.isReturnIfSuspendedCall: Boolean + get() = this is IrCall && this.descriptor.original == returnIfSuspendedDescriptor + } + + private object DECLARATION_ORIGIN_COROUTINE_IMPL : + IrDeclarationOriginImpl("COROUTINE_IMPL") + + private fun IrBuilderWithScope.irVar(descriptor: VariableDescriptor, initializer: IrExpression?) = + IrVariableImpl(startOffset, endOffset, DECLARATION_ORIGIN_COROUTINE_IMPL, descriptor, initializer) + + private fun IrBuilderWithScope.irReturnIfSuspended(value: ValueDescriptor) = + irIfThen(irEqeqeq(irGet(value), irGet(COROUTINE_SUSPENDED)), + irReturn(irGet(value))) + + private fun IrBuilderWithScope.irThrowIfNotNull(exception: ValueDescriptor) = + irIfThen(irNot(irEqeqeq(irGet(exception), irNull())), + irThrow(irImplicitCast(irGet(exception), exception.type.makeNotNullable()))) + } + + private open class VariablesScopeTracker: IrElementVisitorVoid { + + private fun MutableList.push(element: E) = this.add(element) + + private fun MutableList.pop() = this.removeAt(size - 1) + + private fun MutableList.peek(): E? = if (size == 0) null else this[size - 1] + + protected val scopeStack = mutableListOf>(mutableSetOf()) + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitContainerExpression(expression: IrContainerExpression) { + if (!expression.isTransparentScope) + scopeStack.push(mutableSetOf()) + super.visitContainerExpression(expression) + if (!expression.isTransparentScope) + scopeStack.pop() + } + + override fun visitCatch(aCatch: IrCatch) { + scopeStack.push(mutableSetOf(aCatch.parameter)) + super.visitCatch(aCatch) + scopeStack.pop() + } + + override fun visitVariable(declaration: IrVariable) { + super.visitVariable(declaration) + scopeStack.peek()!!.add(declaration.descriptor) + } + } + + fun IrBuilderWithScope.irUnit() = + IrGetObjectValueImpl(startOffset, endOffset, context.builtIns.unitType, context.builtIns.unit) + + fun IrBuilderWithScope.irIfThen(condition: IrExpression, thenPart: IrExpression) = + IrIfThenElseImpl(startOffset, endOffset, context.builtIns.unitType, condition, thenPart, null) + + fun IrBuilderWithScope.irGet(descriptor: PropertyDescriptor) = + IrGetterCallImpl(startOffset, endOffset, descriptor.getter!!, null) + + fun IrBuilderWithScope.irSet(receiver: IrExpression, descriptor: PropertyDescriptor, arg: IrExpression) = + IrSetterCallImpl(startOffset, endOffset, descriptor.setter!!, null, receiver, null, arg) + + fun IrBuilderWithScope.irNot(arg: IrExpression) = + primitiveOp1(startOffset, endOffset, context.irBuiltIns.booleanNot, IrStatementOrigin.EXCL, arg) + + fun IrBuilderWithScope.irThrow(arg: IrExpression) = + IrThrowImpl(startOffset, endOffset, arg.type, arg) + + fun IrBuilderWithScope.irCast(arg: IrExpression, type: KotlinType, typeOperand: KotlinType) = + IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.CAST, typeOperand, arg) + + fun IrBuilderWithScope.irGetField(receiver: IrExpression, descriptor: PropertyDescriptor) = + IrGetFieldImpl(startOffset, endOffset, descriptor, receiver) + + fun IrBuilderWithScope.irSetField(receiver: IrExpression, descriptor: PropertyDescriptor, value: IrExpression) = + IrSetFieldImpl(startOffset, endOffset, descriptor, receiver, value) +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/Coroutines.kt b/runtime/src/main/kotlin/konan/internal/Coroutines.kt index 6db4ec794ce..bca2bd6a1ac 100644 --- a/runtime/src/main/kotlin/konan/internal/Coroutines.kt +++ b/runtime/src/main/kotlin/konan/internal/Coroutines.kt @@ -3,6 +3,14 @@ package konan.internal import kotlin.coroutines.experimental.* import kotlin.coroutines.experimental.intrinsics.* +@Intrinsic +@PublishedApi +internal fun getContinuation(): Continuation = throw AssertionError("Call to getContinuation should've been lowered") + +@Intrinsic +@PublishedApi +internal fun returnIfSuspended(value: Any?): T = throw AssertionError("Call to returnIfSuspended should've been lowered") + // Single-threaded continuation. class SafeContinuation constructor( @@ -67,6 +75,7 @@ internal fun interceptContinuationIfNeeded( * @suppress */ @ExportForCompiler +@PublishedApi internal fun normalizeContinuation(continuation: Continuation): Continuation = (continuation as? CoroutineImpl)?.facade ?: continuation @@ -80,7 +89,8 @@ abstract internal class CoroutineImpl( // label == -1 when coroutine cannot be started (it is just a factory object) or has already finished execution // label == 0 in initial part of the coroutine - protected var label: Int = if (completion != null) 0L else -1L + // TODO: use IntPtr. + protected var label: Long = if (completion != null) 0L else -1L private val _context: CoroutineContext? = completion?.context diff --git a/runtime/src/main/kotlin/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt b/runtime/src/main/kotlin/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt index b5f3288037d..9e6f726d246 100644 --- a/runtime/src/main/kotlin/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt +++ b/runtime/src/main/kotlin/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt @@ -19,8 +19,7 @@ package kotlin.coroutines.experimental.intrinsics import kotlin.coroutines.experimental.Continuation import kotlin.coroutines.experimental.CoroutineContext import kotlin.coroutines.experimental.processBareContinuationResume -import konan.internal.interceptContinuationIfNeeded -import konan.internal.CoroutineImpl +import konan.internal.* /** * Obtains the current continuation instance inside suspend functions and either suspend @@ -40,7 +39,8 @@ import konan.internal.CoroutineImpl * continuation instance. */ @kotlin.internal.InlineOnly -public inline suspend fun suspendCoroutineOrReturn(crossinline block: (Continuation) -> Any?): T = throw Error("Should've been intrinsified") +public inline suspend fun suspendCoroutineOrReturn(crossinline block: (Continuation) -> Any?): T = + returnIfSuspended(block(normalizeContinuation(getContinuation()))) /** * This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that