diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 57337b55a53..ec98f18b31f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -33,7 +33,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD") object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true) object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true) - object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW") object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true) object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true) object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true) @@ -43,6 +42,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object SUSPEND_LAMBDA : IrDeclarationOriginImpl("SUSPEND_LAMBDA") object FOR_INLINE_STATE_MACHINE_TEMPLATE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE") object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE") - object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE_VIEW") object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD") } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 594ff1bf6dc..ed12a4c1949 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.MemoizedInlineClassReplacements +import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.inline.NameGenerator import org.jetbrains.kotlin.codegen.state.GenerationState @@ -108,7 +109,7 @@ class JvmBackendContext( val suspendLambdaToOriginalFunctionMap = mutableMapOf() val continuationClassBuilders = mutableMapOf() val suspendFunctionOriginalToView = mutableMapOf() - val suspendFunctionViewToOriginal = mutableMapOf() + val suspendFunctionOriginalToStub = mutableMapOf() val suspendTailCallsWithUnitReplacement = mutableSetOf() val fakeContinuation: IrExpression = createFakeContinuation(this) @@ -117,8 +118,13 @@ class JvmBackendContext( val inlineClassReplacements = MemoizedInlineClassReplacements() internal fun recordSuspendFunctionView(function: IrFunction, view: IrFunction) { - suspendFunctionOriginalToView[function] = view - suspendFunctionViewToOriginal[view] = function + val attribute = function.suspendFunctionOriginal() + suspendFunctionOriginalToStub.remove(attribute) + suspendFunctionOriginalToView[attribute] = view + } + + internal fun recordSuspendFunctionViewStub(function: IrFunction, stub: IrFunction) { + suspendFunctionOriginalToStub[function.suspendFunctionOriginal()] = stub } internal fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt index 46a84bb927d..3c738c73c3e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt @@ -6,36 +6,29 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.common.CodegenUtil -import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom -import org.jetbrains.kotlin.backend.common.ir.copyValueParametersInsertingContinuationFrom -import org.jetbrains.kotlin.backend.common.ir.isSuspend -import org.jetbrains.kotlin.backend.common.lower.VariableRemapper import org.jetbrains.kotlin.backend.common.lower.allOverridden import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME -import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME import org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker import org.jetbrains.kotlin.config.isReleaseCoroutines -import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter -import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.declarations.name import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.isUnit -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.ir.util.isSuspend +import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature @@ -71,8 +64,9 @@ internal fun generateStateMachineForNamedFunction( || irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION, internalNameForDispatchReceiver = classCodegen.visitor.thisName, putContinuationParameterToLvt = false, - disableTailCallOptimizationForFunctionReturningUnit = irFunction.returnType.isUnit() && - irFunction.anyOfOverriddenFunctionsReturnsNonUnit() + disableTailCallOptimizationForFunctionReturningUnit = irFunction.suspendFunctionOriginal().let { + it.returnType.isUnit() && it.anyOfOverriddenFunctionsReturnsNonUnit() + } ) } @@ -134,109 +128,6 @@ internal fun IrFunction.isKnownToBeTailCall(): Boolean = internal fun IrFunction.shouldNotContainSuspendMarkers(): Boolean = isInvokeSuspendOfContinuation() || isKnownToBeTailCall() -// Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation): Any?` -// the result is called 'view', just to be consistent with old backend. -internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction { - if (!isSuspend || origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW || - origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW - ) return this - return context.suspendFunctionOriginalToView[this] ?: suspendFunctionView(context, true) -} - -fun IrFunction.suspendFunctionView(context: JvmBackendContext, generateBody: Boolean): IrFunction { - require(this.isSuspend && this is IrSimpleFunction) - // For SuspendFunction{N}.invoke we need to generate INVOKEINTERFACE Function{N+1}.invoke(...Ljava/lang/Object;)... - // instead of INVOKEINTERFACE Function{N+1}.invoke(...Lkotlin/coroutines/Continuation;)... - val isInvokeOfNumberedSuspendFunction = (parent as? IrClass)?.defaultType?.isSuspendFunction() == true - // And we need to generate this function for callable references - val isBridgeInvokeOfCallableReference = origin == IrDeclarationOrigin.BRIDGE && - (parent as? IrClass)?.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL - val continuationType = if (isInvokeOfNumberedSuspendFunction || isBridgeInvokeOfCallableReference) - context.irBuiltIns.anyNType - else - context.ir.symbols.continuationClass.createType(false, listOf(makeTypeProjection(returnType, Variance.INVARIANT))) - return buildFunWithDescriptorForInlining(descriptor) { - updateFrom(this@suspendFunctionView) - name = this@suspendFunctionView.name - origin = if (origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) - JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW - else - JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW - returnType = context.irBuiltIns.anyNType - }.also { - it.parent = parent - - it.annotations.addAll(annotations) - - it.copyAttributes(this) - it.copyTypeParametersFrom(this) - - // Copy the value parameters and insert the continuation parameter. The continuation parameter - // goes before the default argument mask(s) and handler for default argument stubs. - // TODO: It would be nice if AddContinuationLowering could insert the continuation argument before default stub generation. - // That would avoid the reshuffling both here and in createSuspendFunctionClassViewIfNeeded. - var continuationValueParam: IrValueParameter? = null - it.copyValueParametersInsertingContinuationFrom(this) { - continuationValueParam = it.addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType) - } - - if (generateBody) { - // Add the suspend function view to the map before transforming the body to make sure - // that recursive suspend functions do not lead to unbounded recursion at compile time. - context.recordSuspendFunctionView(this, it) - - val valueParametersMapping = explicitParameters.zip(it.explicitParameters.filter { it != continuationValueParam }).toMap() - it.body = body?.deepCopyWithSymbols(this) - it.body?.transformChildrenVoid(object : VariableRemapper(valueParametersMapping) { - // Do not cross class boundaries inside functions. Otherwise, callable references will try to access wrong $completion. - override fun visitClass(declaration: IrClass): IrStatement = declaration - - override fun visitCall(expression: IrCall): IrExpression = - super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it, callerIsInlineLambda = false)) - }) - } - } -} - -internal fun IrCall.createSuspendFunctionCallViewIfNeeded( - context: JvmBackendContext, - caller: IrFunction, - callerIsInlineLambda: Boolean -): IrCall { - if (!isSuspend) return this - val view = (symbol.owner as IrSimpleFunction).getOrCreateSuspendFunctionViewIfNeeded(context) - if (view == symbol.owner) return this - return IrCallImpl(startOffset, endOffset, view.returnType, view.symbol, superQualifierSymbol = superQualifierSymbol).also { - it.copyTypeArgumentsFrom(this) - it.dispatchReceiver = dispatchReceiver - it.extensionReceiver = extensionReceiver - // Locate the caller continuation parameter. The continuation parameter is before default argument mask(s) and handler params. - val callerNumberOfMasks = caller.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION } - val callerContinuationIndex = caller.valueParameters.size - 1 - (if (callerNumberOfMasks != 0) callerNumberOfMasks + 1 else 0) - val continuationParameter = - when { - caller.isInvokeSuspendOfLambda() || caller.isInvokeSuspendOfContinuation() || - caller.isInvokeSuspendForInlineOfLambda() -> - IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol) - callerIsInlineLambda -> context.fakeContinuation - else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters[callerContinuationIndex].symbol) - } - // If the suspend function view that we are calling has default parameters, we need to make sure to pass the - // continuation before the default parameter mask(s) and handler. - val numberOfMasks = view.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION } - val continuationIndex = valueArgumentsCount - (if (numberOfMasks != 0) numberOfMasks + 1 else 0) - for (i in 0 until continuationIndex) { - it.putValueArgument(i, getValueArgument(i)) - } - it.putValueArgument(continuationIndex, continuationParameter) - if (numberOfMasks != 0) { - for (i in 0 until numberOfMasks + 1) { - it.putValueArgument(valueArgumentsCount + i - 1, getValueArgument(continuationIndex + i)) - } - } - } -} - internal fun createFakeContinuation(context: JvmBackendContext): IrExpression = IrErrorExpressionImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index d2cfddf86fc..cd5161d5bfb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.* import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry @@ -52,6 +53,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext import org.jetbrains.kotlin.types.model.TypeParameterMarker +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.keysToMap @@ -166,7 +168,7 @@ class ExpressionCodegen( // TODO remove fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue { - if (expression === context.fakeContinuation) { + if (expression.attributeOwnerId === context.fakeContinuation) { addFakeContinuationMarker(mv) } else { expression.accept(this, data).coerce(type, irType).materialize() @@ -219,10 +221,11 @@ class ExpressionCodegen( } private fun generateFakeContinuationConstructorIfNeeded() { - if (irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW) return + if (irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) return val continuationClass = classCodegen.irClass.functions.find { - it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX) - }?.body?.statements?.get(0) ?: error("could not find continuation for ${irFunction.render()}") + it.attributeOwnerId == (irFunction as? IrSimpleFunction)?.attributeOwnerId && + it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX) + }?.body?.statements?.firstIsInstance() ?: error("could not find continuation for ${irFunction.render()}") generateFakeContinuationConstructorCall( mv, classCodegen.visitor, @@ -254,12 +257,10 @@ class ExpressionCodegen( if (notCallableFromJava) return - // Do not generate non-null checks for suspend function views. When resumed the arguments + // Do not generate non-null checks for suspend functions. When resumed the arguments // will be null and the actual values are taken from the continuation. - val isSuspendFunctionView = irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW || - irFunction.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW - if (isSuspendFunctionView) + if (irFunction.isSuspend) return irFunction.extensionReceiverParameter?.let { generateNonNullAssertion(it) } @@ -361,10 +362,6 @@ class ExpressionCodegen( override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) = visitStatementContainer(expression, data).coerce(expression.type) - override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue { - return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction, inlinedInto != null), data) - } - override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue { classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol) ?.invoke(expression, this, data)?.let { return it } @@ -445,7 +442,7 @@ class ExpressionCodegen( // Check return type of non-lowered suspend call, in order to replace the result of the call with Unit, // otherwise, it would seem like the call returns non-unit upon resume. // See box/coroutines/tailCallOptimization/unit tests. - if (context.suspendFunctionViewToOriginal[expression.symbol.owner]?.returnType?.isUnit() == true) { + if (((expression.symbol.owner as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.returnType?.isUnit() == true) { addReturnsUnitMarker(mv) } @@ -486,11 +483,11 @@ class ExpressionCodegen( // Inside $$forInline functions crossinline calls are (usually) not suspension points, otherwise, flow will be pessimized (dispatchReceiver as? IrGetField)?.symbol?.owner?.origin == LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE -> - irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE + irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE // The same goes for inline lambdas (dispatchReceiver as? IrGetValue)?.let { it.origin == IrStatementOrigin.VARIABLE_AS_FUNCTION && (it.symbol.owner as? IrValueParameter)?.isNoinline != true - } == true -> irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE + } == true -> irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE // Otherwise, this is normal inline call, ergo not a suspension point else -> false } @@ -636,10 +633,21 @@ class ExpressionCodegen( override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue { val returnTarget = expression.returnTargetSymbol.owner - val owner = - (returnTarget as? IrFunction + var owner = + returnTarget as? IrFunction ?: (returnTarget as? IrReturnableBlock)?.inlineFunctionSymbol?.owner - ?: error("Unsupported IrReturnTarget: $returnTarget")).getOrCreateSuspendFunctionViewIfNeeded(context) + ?: error("Unsupported IrReturnTarget: $returnTarget") + // $$forInline and $suspendImpl functions share the same attributes as originals, but the name is different + // fix the return target. TODO: attributes seem to be overloaded. We rely on their uniqueness across transformations to find views + // TODO: but in this case, they are not unique enough. + if ((irFunction.origin == FOR_INLINE_STATE_MACHINE_TEMPLATE || + irFunction.origin == SUSPEND_IMPL_STATIC_FUNCTION || + irFunction.origin == FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) && + owner.isSuspend && irFunction is IrSimpleFunction && owner is IrSimpleFunction && + irFunction.attributeOwnerId == owner.attributeOwnerId + ) { + owner = irFunction + } //TODO: should be owner != irFunction val isNonLocalReturn = methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index 213d5117cbb..73321bd23ee 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -53,10 +53,9 @@ open class FunctionCodegen( } private fun doGenerate(): JvmMethodGenericSignature { - val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context) - val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(functionView) + val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(irFunction) - val flags = calculateMethodFlags(functionView.isStatic) + val flags = calculateMethodFlags(irFunction.isStatic) var methodVisitor = createMethod(flags, signature) if (state.generateParametersMetadata && flags.and(Opcodes.ACC_SYNTHETIC) == 0) { @@ -75,14 +74,14 @@ open class FunctionCodegen( ) } }.genAnnotations( - functionView, + irFunction, signature.asmMethod.returnType, irFunction.returnType ) // Not generating parameter annotations for default stubs fixes KT-7892, though // this certainly looks like a workaround for a javac bug. if (irFunction !is IrConstructor || !irFunction.parentAsClass.shouldNotGenerateConstructorParameterAnnotations()) { - generateParameterAnnotations(functionView, methodVisitor, signature, classCodegen, context) + generateParameterAnnotations(irFunction, methodVisitor, signature, classCodegen, context) } } @@ -107,7 +106,7 @@ open class FunctionCodegen( ) else -> methodVisitor } - ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate() + ExpressionCodegen(irFunction, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate() methodVisitor.visitMaxs(-1, -1) if (irFunction.hasContinuation()) { context.continuationClassBuilders[continuationClass().attributeOwnerId].sure { @@ -151,7 +150,8 @@ open class FunctionCodegen( // $$forInline functions never have a continuation origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE - private fun continuationClass() = irFunction.body!!.statements.firstIsInstance() + private fun continuationClass() = + irFunction.body!!.statements.firstIsInstance() private fun IrFunction.getVisibilityForDefaultArgumentStub(): Int = when (visibility) { @@ -260,17 +260,16 @@ open class FunctionCodegen( private fun createFrameMapWithReceivers(): IrFrameMap { val frameMap = IrFrameMap() - val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context) if (irFunction is IrConstructor) { frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!) - } else if (functionView.dispatchReceiverParameter != null) { - frameMap.enterDispatchReceiver(functionView.dispatchReceiverParameter!!) + } else if (irFunction.dispatchReceiverParameter != null) { + frameMap.enterDispatchReceiver(irFunction.dispatchReceiverParameter!!) } - functionView.extensionReceiverParameter?.let { + irFunction.extensionReceiverParameter?.let { frameMap.enter(it, classCodegen.typeMapper.mapType(it)) } - for (parameter in functionView.valueParameters) { + for (parameter in irFunction.valueParameters) { frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter.type)) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index d3f9bc31b20..c2cf9be8fc0 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -1,24 +1,22 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.backend.jvm.codegen import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionViewOrStub import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter import org.jetbrains.kotlin.backend.jvm.ir.isLambda -import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.types.IrType @@ -251,7 +249,7 @@ class IrExpressionLambdaImpl( capturedParamDesc(param.name.asString(), typeMapper.mapType(param.type)) } - private val loweredMethod = methodSignatureMapper.mapAsmMethod(function.getOrCreateSuspendFunctionViewIfNeeded(context)) + private val loweredMethod = methodSignatureMapper.mapAsmMethod(function.suspendFunctionViewOrStub(context)) val capturedParamsInDesc: List = if (isBoundCallableReference) { loweredMethod.argumentTypes.take(1) @@ -274,7 +272,7 @@ class IrExpressionLambdaImpl( override val hasDispatchReceiver: Boolean = false override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor { - return function.getOrCreateSuspendFunctionViewIfNeeded(context).descriptor + return function.suspendFunctionViewOrStub(context).descriptor } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt index 77c1257cfe5..0d11e944822 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.ir.ir2string +import org.jetbrains.kotlin.backend.jvm.lower.getOrCreateSuspendFunctionViewIfNeeded import org.jetbrains.kotlin.codegen.BaseExpressionCodegen import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.OwnerKind @@ -86,7 +87,7 @@ class IrSourceCompilerForInline( function.parentAsClass.functions.find { it.name.asString() == function.name.asString() + FOR_INLINE_SUFFIX && it.attributeOwnerId == (function as? IrAttributeContainer)?.attributeOwnerId - } ?: function + } ?: function.getOrCreateSuspendFunctionViewIfNeeded(classCodegen.context) else function val functionCodegen = object : FunctionCodegen(forInlineFunction, classCodegen, codegen.takeIf { isLambda }) { override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 437036ce233..4d8376f0904 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor +import org.jetbrains.kotlin.backend.jvm.lower.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil @@ -208,13 +209,6 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { } } - if (function.isSuspend && function.origin != JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW && - function.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW && - (function.parent as? IrClass)?.origin != JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL - ) { - return mapSignature(function.suspendFunctionView(context, false), skipGenericSignature) - } - val sw = if (skipGenericSignature) JvmSignatureWriter() else BothSignatureWriter(BothSignatureWriter.Mode.METHOD) typeMapper.writeFormalTypeParameters(function.typeParameters, sw) @@ -300,7 +294,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { } fun mapToCallableMethod(caller: IrFunction, expression: IrFunctionAccessExpression): IrCallableMethod { - val callee = expression.symbol.owner.getOrCreateSuspendFunctionViewIfNeeded(context) + val callee = expression.symbol.owner val calleeParent = callee.parentAsClass val owner = typeMapper.mapClass(calleeParent) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt index 8ea93319c87..594c6d956dd 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -14,7 +14,9 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator import org.jetbrains.kotlin.backend.jvm.ir.defaultValue -import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrBlock +import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendForInlineOfLambda +import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendOfContinuation +import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendOfLambda import org.jetbrains.kotlin.backend.jvm.localDeclarationsPhase import org.jetbrains.kotlin.codegen.coroutines.* import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX @@ -28,9 +30,7 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl @@ -47,19 +47,45 @@ import org.jetbrains.kotlin.util.OperatorNameConventions internal val addContinuationPhase = makeIrFilePhase( ::AddContinuationLowering, "AddContinuation", - "Add continuation classes to suspend functions and transform suspend lambdas into continuations", + "Add continuation classes and parameters to suspend functions and transform suspend lambdas into continuations", prerequisite = setOf(localDeclarationsPhase, tailCallOptimizationPhase) ) private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass { - override fun lower(irFile: IrFile) { - val (suspendLambdas, inlineLambdas) = markSuspendLambdas(irFile) - transformSuspendFunctions(irFile, (suspendLambdas.map { it.function } + inlineLambdas).toSet()) - transformReferencesToSuspendLambdas(irFile, suspendLambdas) + val suspendLambdas = findSuspendAndInlineLambdas(irFile) + addContinuationObjectAndContinuationParameterToSuspendFunctions(irFile) + transformSuspendLambdasIntoContinuations(irFile, suspendLambdas) + addContinuationParameterToSuspendCallsAndUpdateNonLocalReturns(irFile) } - private fun transformReferencesToSuspendLambdas(irFile: IrFile, suspendLambdas: List) { + private fun addContinuationParameterToSuspendCallsAndUpdateNonLocalReturns(irFile: IrFile) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + val functionStack = mutableListOf() + + override fun visitFunction(declaration: IrFunction): IrStatement { + functionStack.push(declaration) + return super.visitFunction(declaration).also { functionStack.pop() } + } + + override fun visitCall(expression: IrCall): IrExpression { + // This is a property, no need to add continuation parameter, since this cannot be suspend call + if (functionStack.isEmpty()) return super.visitCall(expression) + val caller = functionStack.peek()!! + return (super.visitCall(expression) as IrCall) + .createSuspendFunctionCallViewIfNeeded(context, caller) + } + + override fun visitReturn(expression: IrReturn): IrExpression { + val ret = super.visitReturn(expression) as IrReturn + val irFunction = expression.returnTargetSymbol.owner as? IrFunction ?: return ret + val targetViewOrStub = irFunction.suspendFunctionViewOrStub(context) + return IrReturnImpl(ret.startOffset, ret.endOffset, ret.type, targetViewOrStub.symbol, ret.value) + } + }) + } + + private fun transformSuspendLambdasIntoContinuations(irFile: IrFile, suspendLambdas: List) { for (lambda in suspendLambdas) { (lambda.function.parent as IrDeclarationContainer).declarations.remove(lambda.function) } @@ -508,9 +534,13 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : UNDEFINED_OFFSET, UNDEFINED_OFFSET, backendContext ) } - for ((i, parameter) in irFunction.valueParameters.withIndex()) { + for ((i, parameter) in irFunction.valueParameters.dropLast(1).withIndex()) { it.putValueArgument(i, parameter.type.defaultValue(UNDEFINED_OFFSET, UNDEFINED_OFFSET, backendContext)) } + it.putValueArgument( + irFunction.valueParameters.size - 1, + IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.dispatchReceiverParameter!!.symbol) + ) if (isStaticSuspendImpl) { it.putValueArgument(0, capturedThisValue) } @@ -529,6 +559,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : copyMetadata = false ) static.body = irFunction.moveBodyTo(static) + static.copyAttributes(irFunction) // Rewrite the body of the original suspend method to forward to the new static method. irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody { +irReturn(irCall(static).also { @@ -547,11 +578,10 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : } }) } - static.copyAttributes(irFunction) return static } - private fun transformSuspendFunctions(irFile: IrFile, suspendAndInlineLambdas: Set) { + private fun addContinuationObjectAndContinuationParameterToSuspendFunctions(irFile: IrFile) { irFile.transformChildrenVoid(object : IrElementTransformerVoid() { private val functionsStack = arrayListOf() private val suspendFunctionsCapturingCrossinline = mutableSetOf() @@ -573,39 +603,45 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : functionsStack.pop() if (skip(function)) return function - function as IrSimpleFunction + // TODO: This does not work for DEFAULT_IMPLS + val view = + (if (function.body == null) function.suspendFunctionStub(context, false) + else function.getOrCreateSuspendFunctionViewIfNeeded(context)) as IrSimpleFunction + + if (withoutContinuationClass(function)) return view + if (function in suspendFunctionsCapturingCrossinline || function.isInline) { - val newFunction = buildFunWithDescriptorForInlining(function.descriptor) { - name = Name.identifier(function.name.asString() + FOR_INLINE_SUFFIX) - returnType = function.returnType - modality = function.modality - isSuspend = function.isSuspend - isInline = function.isInline + val newFunction = buildFunWithDescriptorForInlining(view.descriptor) { + name = Name.identifier(view.name.asString() + FOR_INLINE_SUFFIX) + returnType = view.returnType + modality = view.modality + isSuspend = view.isSuspend + isInline = view.isInline origin = - if (function.isInline) JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE + if (view.isInline) JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE else JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE }.apply { - copyTypeParameters(function.typeParameters) - dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this) - extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(this) - function.valueParameters.mapTo(valueParameters) { it.copyTo(this) } - body = function.copyBodyTo(this) - copyAttributes(function) + copyTypeParameters(view.typeParameters) + dispatchReceiverParameter = view.dispatchReceiverParameter?.copyTo(this) + extensionReceiverParameter = view.extensionReceiverParameter?.copyTo(this) + view.valueParameters.mapTo(valueParameters) { it.copyTo(this) } + body = view.copyBodyTo(this) + copyAttributes(view) } registerNewFunction(newFunction) } - val newFunction = if (function.isOverridable) { + val newFunction = if ((function as IrSimpleFunction).isOverridable) { // Create static method for the suspend state machine method so that reentering the method // does not lead to virtual dispatch to the wrong method. - registerNewFunction(function) - createStaticSuspendImpl(function) - } else function + registerNewFunction(view) + createStaticSuspendImpl(view) + } else view newFunction.body = context.createIrBuilder(newFunction.symbol).irBlockBody { +generateContinuationClassForNamedFunction( newFunction, - function.dispatchReceiverParameter, + view.dispatchReceiverParameter, declaration as IrAttributeContainer ) for (statement in newFunction.body!!.statements) { @@ -615,11 +651,16 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : return newFunction } - private fun skip(function: IrFunction) = - !function.isSuspend || function in suspendAndInlineLambdas || function.body == null || - function.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE || + // TODO: Merge with `knownToBeTailCall` + private fun withoutContinuationClass(function: IrFunction): Boolean = + function.body == null || + function.parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || - function.parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL + function.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE + + private fun skip(function: IrFunction) = + !function.isSuspend || + function.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA private fun registerNewFunction(function: IrFunction) { functionsToAdd.peek()!!.add(function) @@ -638,7 +679,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : }) } - private fun markSuspendLambdas(irElement: IrElement): Pair, List> { + private fun findSuspendAndInlineLambdas(irElement: IrElement): List { val suspendLambdas = arrayListOf() val capturesCrossinline = mutableSetOf() val inlineReferences = mutableSetOf() @@ -678,7 +719,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : } } }) - return suspendLambdas to inlineReferences.map { it.symbol.owner as IrFunction } + return suspendLambdas } private class SuspendLambdaInfo( @@ -689,4 +730,126 @@ private class AddContinuationLowering(private val context: JvmBackendContext) : ) { lateinit var constructor: IrConstructor } +} + +// Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation): Any?` +// the result is called 'view', just to be consistent with old backend. +internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction { + if (!isSuspend) return this + return context.suspendFunctionOriginalToView[suspendFunctionOriginal()] ?: suspendFunctionView(context) +} + +private fun IrFunction.getOrCreateSuspendFunctionStub(context: JvmBackendContext): IrFunction { + if (!isSuspend) return this + return context.suspendFunctionOriginalToStub[suspendFunctionOriginal()] ?: suspendFunctionStub(context) +} + +internal fun IrFunction.suspendFunctionOriginal(): IrFunction { + require(isSuspend && this is IrSimpleFunction) + return attributeOwnerId as IrFunction +} + +private fun IrFunction.suspendFunctionStub(context: JvmBackendContext): IrFunction { + require(this.isSuspend && this is IrSimpleFunction) + return buildFunWithDescriptorForInlining(descriptor) { + updateFrom(this@suspendFunctionStub) + name = this@suspendFunctionStub.name + origin = this@suspendFunctionStub.origin + returnType = context.irBuiltIns.anyNType + }.also { function -> + function.parent = parent + + function.annotations.addAll(annotations) + function.metadata = metadata + + function.copyAttributes(this) + function.copyTypeParametersFrom(this) + + if (origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE) { + function.overriddenSymbols + .addAll(overriddenSymbols.map { it.owner.suspendFunctionViewOrStub(context).symbol as IrSimpleFunctionSymbol }) + } + + // Copy the value parameters and insert the continuation parameter. The continuation parameter + // goes before the default argument mask(s) and handler for default argument stubs. + // TODO: It would be nice if AddContinuationLowering could insert the continuation argument before default stub generation. + // That would avoid the reshuffling both here and in createSuspendFunctionCallViewIfNeeded. + function.copyValueParametersInsertingContinuationFrom(this) { + function.addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType(context)) + } + + context.recordSuspendFunctionViewStub(this, function) + } +} + +private fun IrFunction.continuationType(context: JvmBackendContext): IrType { + // For SuspendFunction{N}.invoke we need to generate INVOKEINTERFACE Function{N+1}.invoke(...Ljava/lang/Object;)... + // instead of INVOKEINTERFACE Function{N+1}.invoke(...Lkotlin/coroutines/Continuation;)... + val isInvokeOfNumberedSuspendFunction = (parent as? IrClass)?.defaultType?.isSuspendFunction() == true + val isInvokeOfNumberedFunction = (parent as? IrClass)?.fqNameWhenAvailable?.asString()?.let { + it.startsWith("kotlin.jvm.functions.Function") && it.removePrefix("kotlin.jvm.functions.Function").all { c -> c.isDigit() } + } == true + return if (isInvokeOfNumberedSuspendFunction || isInvokeOfNumberedFunction) + context.irBuiltIns.anyNType + else + context.ir.symbols.continuationClass.typeWith(returnType) +} + +private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFunction { + require(isSuspend && this is IrSimpleFunction) + return getOrCreateSuspendFunctionStub(context).also { function -> + context.recordSuspendFunctionView(this, function) + + val continuationParameter = + function.valueParameters.find { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION } + ?.let { function.valueParameters[it.index - 1] } ?: function.valueParameters.last() + + function.body = + moveBodyTo(function, explicitParameters.zip(function.explicitParameters.filter { it != continuationParameter }).toMap()) + } +} + +fun IrFunction.suspendFunctionViewOrStub(context: JvmBackendContext): IrFunction { + if (!isSuspend) return this + return context.suspendFunctionOriginalToView[suspendFunctionOriginal()] ?: getOrCreateSuspendFunctionStub(context) +} + +private fun IrCall.createSuspendFunctionCallViewIfNeeded(context: JvmBackendContext, caller: IrFunction): IrCall { + // Calls inside continuation are already generated with continuation parameter as well as calls to suspendImpls + if (!isSuspend || caller.isInvokeSuspendOfContinuation() + || symbol.owner.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION + || (symbol.owner.valueParameters.lastOrNull()?.name?.asString() == SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME) + ) return this + val view = (symbol.owner as IrSimpleFunction).suspendFunctionViewOrStub(context) + if (view == symbol.owner) return this + + return IrCallImpl(startOffset, endOffset, view.returnType, view.symbol, superQualifierSymbol = superQualifierSymbol).also { + it.copyTypeArgumentsFrom(this) + it.copyAttributes(this) + it.dispatchReceiver = dispatchReceiver + it.extensionReceiver = extensionReceiver + val callerNumberOfMasks = caller.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION } + val callerContinuationIndex = caller.valueParameters.size - 1 - (if (callerNumberOfMasks != 0) callerNumberOfMasks + 1 else 0) + val continuationParameter = + when { + caller.isInvokeSuspendOfLambda() || caller.isInvokeSuspendForInlineOfLambda() -> + IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol) + // At this point the only LOCAL_FUNCTION_FOR_LAMBDAs are inline and crossinline lambdas + caller.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA -> context.fakeContinuation + else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters[callerContinuationIndex].symbol) + } + // If the suspend function view that we are calling has default parameters, we need to make sure to pass the + // continuation before the default parameter mask(s) and handler. + val numberOfMasks = view.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION } + val continuationIndex = valueArgumentsCount - (if (numberOfMasks != 0) numberOfMasks + 1 else 0) + for (i in 0 until continuationIndex) { + it.putValueArgument(i, getValueArgument(i)) + } + it.putValueArgument(continuationIndex, continuationParameter) + if (numberOfMasks != 0) { + for (i in 0 until numberOfMasks + 1) { + it.putValueArgument(valueArgumentsCount + i - 1, getValueArgument(continuationIndex + i)) + } + } + } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index ba9d6893fdc..cab81294377 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -37,10 +37,13 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.Type @@ -424,7 +427,11 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass IrValueParameterSymbolImpl(descriptor), name, index, - type.eraseTypeParameters(), + // SuspendFunction{N} is Function{N+1} at runtime, thus, when we generate a bridge for suspend callable reference, + // we need to replace type of its continuation parameter with Any? + if (target.isSuspend && type.eraseTypeParameters().getClass() + ?.fqNameWhenAvailable == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_RELEASE + ) context.irBuiltIns.anyNType else type.eraseTypeParameters(), varargElementType?.eraseTypeParameters(), isCrossinline, isNoinline diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt index 41d07c46fbb..557afbad13e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -88,7 +88,7 @@ private class FunctionNVarargBridgeLowering(val context: JvmBackendContext) : // Add vararg invoke bridge val invokeFunction = declaration.functions.single { - it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - 1 + it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - if (it.isSuspend) 0 else 1 } invokeFunction.overriddenSymbols.clear() declaration.addBridge(invokeFunction, functionNInvokeFun.owner) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt index 717c7b8e087..31074016cc2 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2016 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. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.ir.util @@ -179,6 +168,7 @@ open class DeepCopyIrTreeWithSymbols( declaration.overriddenSymbols.mapTo(overriddenSymbols) { symbolRemapper.getReferencedFunction(it) as IrSimpleFunctionSymbol } + copyAttributes(declaration) transformFunctionChildren(declaration) } diff --git a/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt b/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt index 11c29f7ddd1..a5ca1c9b07d 100644 --- a/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt +++ b/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR +// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/superCallInterface.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/superCallInterface.kt index 8608725b255..7e163b01b62 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/superCallInterface.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/superCallInterface.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR +// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_ir.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_ir.txt index 116814ea318..6538475e7bb 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_ir.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_ir.txt @@ -6,6 +6,7 @@ final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 { field L$2: java.lang.Object field L$3: java.lang.Object field L$4: java.lang.Object + field L$5: java.lang.Object field label: int @org.jetbrains.annotations.NotNull field result: java.lang.Object synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1 @@ -121,6 +122,7 @@ final class CrossinlineKt$filter$$inlined$source$1$1 { field L$2: java.lang.Object field L$3: java.lang.Object field L$4: java.lang.Object + field L$5: java.lang.Object field label: int @org.jetbrains.annotations.NotNull field result: java.lang.Object synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1 @@ -146,6 +148,7 @@ final class CrossinlineKt$filter$$inlined$source$2$1 { field L$2: java.lang.Object field L$3: java.lang.Object field L$4: java.lang.Object + field L$5: java.lang.Object field label: int @org.jetbrains.annotations.NotNull field result: java.lang.Object synthetic final field this$0: CrossinlineKt$filter$$inlined$source$2 @@ -310,7 +313,6 @@ final class CrossinlineKt$range$$inlined$source$1$1 { field L$1: java.lang.Object field L$2: java.lang.Object field L$3: java.lang.Object - field L$4: java.lang.Object field label: int @org.jetbrains.annotations.NotNull field result: java.lang.Object synthetic final field this$0: CrossinlineKt$range$$inlined$source$1 diff --git a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueCrossinline.kt b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueCrossinline.kt index 320fba9186b..3f4bcfc9006 100644 --- a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueCrossinline.kt +++ b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueCrossinline.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: test.kt // COMMON_COROUTINES_TEST // WITH_RUNTIME diff --git a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInline.kt b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInline.kt index 9b0dfc04f8c..af2a989202a 100644 --- a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInline.kt +++ b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInline.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: test.kt // COMMON_COROUTINES_TEST // WITH_RUNTIME diff --git a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInlineFromMultiFileFacade.kt b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInlineFromMultiFileFacade.kt index a449c55b285..a4382510c57 100644 --- a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInlineFromMultiFileFacade.kt +++ b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultValueInlineFromMultiFileFacade.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: test.kt // COMMON_COROUTINES_TEST // WITH_RUNTIME