diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt index 58fd88aa754..5dd5bab6b20 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt @@ -12,6 +12,7 @@ import kotlin.reflect.KProperty interface Mapping { val defaultArgumentsDispatchFunction: Delegate val defaultArgumentsOriginalFunction: Delegate + val suspendFunctionToCoroutineConstructor: Delegate val lateInitFieldToNullableField: Delegate val inlineClassMemberToStatic: Delegate @@ -32,6 +33,7 @@ open class DefaultMapping : Mapping { override val defaultArgumentsDispatchFunction: Mapping.Delegate = newMapping() override val defaultArgumentsOriginalFunction: Mapping.Delegate = newMapping() + override val suspendFunctionToCoroutineConstructor: Mapping.Delegate = newMapping() override val lateInitFieldToNullableField: Mapping.Delegate = newMapping() override val inlineClassMemberToStatic: Mapping.Delegate = newMapping() diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt index 5a9afbbcdd1..239b528f745 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt @@ -38,7 +38,9 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name -abstract class AbstractSuspendFunctionsLowering(val context: C) : FileLoweringPass { +abstract class AbstractSuspendFunctionsLowering(val context: C) : BodyLoweringPass { + + private var IrFunction.coroutineConstructor by context.mapping.suspendFunctionToCoroutineConstructor protected object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL") protected object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL") @@ -61,53 +63,38 @@ abstract class AbstractSuspendFunctionsLowering(val co delegatingCall private val builtCoroutines = mutableMapOf() - private val suspendLambdas = mutableMapOf() - override fun lower(irFile: IrFile) { - markSuspendLambdas(irFile) - buildCoroutines(irFile) - transformCallableReferencesToSuspendLambdas(irFile) + override fun lower(irBody: IrBody, container: IrDeclaration) { + transformCallableReferencesToSuspendLambdas(irBody) + + if (container.isLambda) return + + tryTransformSuspendFunction(container, null) } - private fun buildCoroutines(irFile: IrFile) { - irFile.transformDeclarationsFlat(::tryTransformSuspendFunction) - irFile.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitClass(declaration: IrClass) { - declaration.acceptChildrenVoid(this) - declaration.transformDeclarationsFlat(::tryTransformSuspendFunction) - } - }) - } - - // Suppress since it is used in native @Suppress("MemberVisibilityCanBePrivate") protected fun IrCall.isReturnIfSuspendedCall() = symbol.owner.run { fqNameWhenAvailable == context.internalPackageFqn.child(Name.identifier("returnIfSuspended")) } - private fun tryTransformSuspendFunction(element: IrElement) = - if (element is IrSimpleFunction && element.isSuspend && element.modality != Modality.ABSTRACT) - transformSuspendFunction(element, suspendLambdas[element]) - else null + private fun tryTransformSuspendFunction(element: IrElement, functionReference: IrFunctionReference?) { + if (element is IrSimpleFunction && element.isSuspend && element.modality != Modality.ABSTRACT) { - private fun markSuspendLambdas(irElement: IrElement) { - irElement.acceptChildrenVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } + transformCallableReferencesToSuspendLambdas(element) - override fun visitFunctionReference(expression: IrFunctionReference) { - expression.acceptChildrenVoid(this) + transformSuspendFunction(element, functionReference)?.let { result -> + result.forEach { declaration -> + if (declaration !== element) { + // TODO Use proper means to emerge declarations + element.file.declarations += declaration + declaration.parent = element.file - if (expression.isSuspend) { - suspendLambdas[expression.symbol.owner] = expression + // TODO investigate IrJsCodegenBoxTestGenerated$Coroutines$ControlFlow.testBreakFinally_1_3 + declaration.patchDeclarationParents(declaration.parent) + } } } - }) + } } private fun transformCallableReferencesToSuspendLambdas(irElement: IrElement) { @@ -118,16 +105,33 @@ abstract class AbstractSuspendFunctionsLowering(val co if (!expression.isSuspend) return expression - val coroutine = builtCoroutines[expression.symbol.owner] - ?: throw Error("Non-local callable reference to suspend lambda: $expression") - val constructorParameters = coroutine.coroutineConstructor.valueParameters + + val coroutineConstructor = if (expression.symbol.owner.isLambda) { + if (expression.symbol.owner in builtCoroutines) { + error("Lambda revisiting?") + } + + tryTransformSuspendFunction(expression.symbol.owner, expression) + + builtCoroutines[expression.symbol.owner]?.coroutineConstructor + ?: throw Error("Non-local callable reference to suspend lambda: $expression") + } else { + expression.symbol.owner.coroutineConstructor ?: run { + + tryTransformSuspendFunction(expression.symbol.owner, expression) + + builtCoroutines[expression.symbol.owner]!!.coroutineConstructor + } + } + + val constructorParameters = coroutineConstructor.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(expression.symbol, expression.startOffset, expression.endOffset) irBuilder.run { - return irCall(coroutine.coroutineConstructor.symbol).apply { + return irCall(coroutineConstructor.symbol).apply { expressionArguments.forEachIndexed { index, argument -> putValueArgument(index, argument) } @@ -144,7 +148,7 @@ abstract class AbstractSuspendFunctionsLowering(val co } private fun transformSuspendFunction(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?) = - when (val suspendFunctionKind = getSuspendFunctionKind(irFunction)) { + when (val suspendFunctionKind = getSuspendFunctionKind(irFunction, functionReference)) { is SuspendFunctionKind.NO_SUSPEND_CALLS -> { null // No suspend function calls - just an ordinary function. } @@ -156,15 +160,15 @@ abstract class AbstractSuspendFunctionsLowering(val co is SuspendFunctionKind.NEEDS_STATE_MACHINE -> { val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation. - if (irFunction in suspendLambdas) // Suspend lambdas are called through factory method , + if (irFunction.isLambda) // Suspend lambdas are called through factory method , listOf(coroutine) // thus we can eliminate original body. else listOf(coroutine, irFunction) } } - private fun getSuspendFunctionKind(irFunction: IrSimpleFunction): SuspendFunctionKind { - if (irFunction in suspendLambdas) + private fun getSuspendFunctionKind(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?): SuspendFunctionKind { + if (irFunction.isLambda || functionReference != null) return SuspendFunctionKind.NEEDS_STATE_MACHINE // Suspend lambdas always need coroutine implementation. val body = irFunction.body ?: return SuspendFunctionKind.NO_SUSPEND_CALLS @@ -237,29 +241,33 @@ abstract class AbstractSuspendFunctionsLowering(val co private fun buildCoroutine(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?): IrClass { val coroutine = CoroutineBuilder(irFunction, functionReference).build() builtCoroutines[irFunction] = coroutine + irFunction.coroutineConstructor = coroutine.coroutineConstructor - if (functionReference == null) { + if (!irFunction.isLambda && functionReference == null) { // It is not a lambda - replace original function with a call to constructor of the built coroutine. val irBuilder = context.createIrBuilder(irFunction.symbol, irFunction.startOffset, irFunction.endOffset) - irFunction.body = irBuilder.irBlockBody(irFunction) { - val constructor = coroutine.coroutineConstructor - generateCoroutineStart(coroutine.stateMachineFunction, - irCallConstructor(constructor.symbol, irFunction.typeParameters.map { - IrSimpleTypeImpl(it.symbol, true, emptyList(), emptyList()) - }).apply { - val functionParameters = irFunction.explicitParameters - functionParameters.forEachIndexed { index, argument -> - putValueArgument(index, irGet(argument)) - } - putValueArgument( - functionParameters.size, - irCall( - getContinuationSymbol, - getContinuationSymbol.owner.returnType, - listOf(irFunction.returnType) + (irFunction.body as IrBlockBody).statements.let { + it.clear() + it += irBuilder.irBlockBody(irFunction) { + val constructor = coroutine.coroutineConstructor + generateCoroutineStart(coroutine.stateMachineFunction, + irCallConstructor(constructor.symbol, irFunction.typeParameters.map { + IrSimpleTypeImpl(it.symbol, true, emptyList(), emptyList()) + }).apply { + val functionParameters = irFunction.explicitParameters + functionParameters.forEachIndexed { index, argument -> + putValueArgument(index, irGet(argument)) + } + putValueArgument( + functionParameters.size, + irCall( + getContinuationSymbol, + getContinuationSymbol.owner.returnType, + listOf(irFunction.returnType) + ) ) - ) - }) + }) + }.statements } } @@ -404,12 +412,14 @@ abstract class AbstractSuspendFunctionsLowering(val co coroutineConstructors += this valueParameters = functionParameters.mapIndexed { index, parameter -> - parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index) + parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index, defaultValue = null) } val continuationParameter = coroutineBaseClassConstructor.valueParameters[0] valueParameters += continuationParameter.copyTo( this, DECLARATION_ORIGIN_COROUTINE_IMPL, - index = valueParameters.size, type = continuationType + index = valueParameters.size, + type = continuationType, + defaultValue = null ) val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset) @@ -695,3 +705,13 @@ abstract class AbstractSuspendFunctionsLowering(val co } } } + +private val IrDeclaration.isLambda + get() = origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA + +class RemoveSuspendLambdas() : DeclarationTransformer { + + override fun transformFlat(declaration: IrDeclaration): List? { + return if (declaration.isLambda && declaration is IrFunction && declaration.isSuspend) emptyList() else null + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index f8c940ff73f..1574588d791 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -19,7 +19,6 @@ import org.jetbrains.kotlin.ir.backend.js.lower.inline.CopyInlineFunctionBodyLow import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.util.patchDeclarationParents private fun DeclarationContainerLoweringPass.runOnFilesPostfix(files: Iterable) = files.forEach { runOnFilePostfix(it) } @@ -283,6 +282,12 @@ private val suspendFunctionsLoweringPhase = makeJsModulePhase( description = "Transform suspend functions into CoroutineImpl instance and build state machine" ) +private val suspendLambdasRemovalLoweringPhase = makeJsModulePhase( + { RemoveSuspendLambdas() }, + name = "RemoveSuspendLambdas", + description = "Remove suspend lambdas" +) + private val privateMembersLoweringPhase = makeJsModulePhase( ::PrivateMembersLowering, name = "PrivateMembersLowering", @@ -534,6 +539,7 @@ val jsPhases = namedIrModulePhase( enumUsageLoweringPhase then enumEntryRemovalLoweringPhase then suspendFunctionsLoweringPhase then + suspendLambdasRemovalLoweringPhase then returnableBlockLoweringPhase then forLoopsLoweringPhase then primitiveCompanionLoweringPhase then diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/bound/emptyLHS.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/bound/emptyLHS.kt index 787975fd0fc..05127d7720e 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/bound/emptyLHS.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/bound/emptyLHS.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR +// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/longArgs.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/longArgs.kt index b2a9e130863..616b590d6a9 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/longArgs.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/longArgs.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR +// IGNORE_BACKEND: JS_IR // WITH_COROUTINES // WITH_RUNTIME