JVM_IR: Do not box/unbox result in continuations
Otherwise, it leads to CCE from kotlin/Result.
This commit is contained in:
+10
-13
@@ -15,10 +15,10 @@ 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.config.coroutinesPackageFqName
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
@@ -106,12 +105,12 @@ internal fun IrFunction.isInvokeSuspendOfContinuation(context: JvmBackendContext
|
||||
// Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation<RetType>): Any?`
|
||||
// the result is called 'view', just to be consistent with old backend.
|
||||
internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction {
|
||||
if (!isSuspend) return this
|
||||
// TODO: Optimize
|
||||
if (context.suspendFunctionViews.values.contains(this)) return this
|
||||
if (!isSuspend || origin == SUSPEND_FUNCTION_VIEW) return this
|
||||
return if (isSuspend) context.suspendFunctionViews.getOrPut(this) { suspendFunctionView(context) } else this
|
||||
}
|
||||
|
||||
private object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
|
||||
|
||||
private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFunction {
|
||||
require(this.isSuspend && this is IrSimpleFunction)
|
||||
val originalDescriptor = this.descriptor
|
||||
@@ -121,7 +120,7 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti
|
||||
else
|
||||
WrappedSimpleFunctionDescriptor(sourceElement = originalDescriptor.source)
|
||||
return IrFunctionImpl(
|
||||
startOffset, endOffset, origin, IrSimpleFunctionSymbolImpl(descriptor),
|
||||
startOffset, endOffset, SUSPEND_FUNCTION_VIEW, IrSimpleFunctionSymbolImpl(descriptor),
|
||||
name, visibility, modality, context.irBuiltIns.anyNType,
|
||||
isInline, isExternal, isTailrec, isSuspend
|
||||
).also {
|
||||
@@ -134,11 +133,8 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti
|
||||
|
||||
valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
|
||||
it.addValueParameter(
|
||||
SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, context.getTopLevelClass(
|
||||
context.state.languageVersionSettings.coroutinesPackageFqName().child(
|
||||
Name.identifier("Continuation")
|
||||
)
|
||||
).createType(false, listOf(makeTypeProjection(returnType, Variance.INVARIANT)))
|
||||
SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, context.ir.symbols.continuationClass
|
||||
.createType(false, listOf(makeTypeProjection(returnType, Variance.INVARIANT)))
|
||||
)
|
||||
val valueParametersMapping = explicitParameters.zip(it.explicitParameters).toMap()
|
||||
it.body = body?.deepCopyWithSymbols(this)
|
||||
@@ -150,19 +146,20 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
if (!expression.isSuspend) return super.visitCall(expression)
|
||||
return super.visitCall(expression.createView(context, it))
|
||||
return super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrCall.createView(context: JvmBackendContext, caller: IrFunction): IrCall {
|
||||
internal fun IrCall.createSuspendFunctionCallViewIfNeeded(context: JvmBackendContext, caller: IrFunction): 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).also {
|
||||
it.copyTypeArgumentsFrom(this)
|
||||
it.dispatchReceiver = dispatchReceiver
|
||||
it.extensionReceiver = extensionReceiver
|
||||
for (i in 0 until valueArgumentsCount) {
|
||||
it.putValueArgument(i, getValueArgument(i))
|
||||
}
|
||||
|
||||
+4
-2
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.CallGenerator
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
||||
import org.jetbrains.kotlin.codegen.extractReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker
|
||||
@@ -297,8 +296,11 @@ 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), data)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
val expression = if (expression is IrCall) expression.createView(context, irFunction) else expression
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it.coerce(expression.type) }
|
||||
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ open class FunctionCodegen(
|
||||
methodVisitor = when {
|
||||
irFunction.isSuspend &&
|
||||
// We do not generate continuation and state-machine for synthetic accessors, in a sense, they are tail-call
|
||||
!irFunction.isDefault() &&
|
||||
!irFunction.isKnownToBeTailCall() &&
|
||||
// TODO: We should generate two versions of inline suspend function: one with state-machine and one without
|
||||
!irFunction.isInline ->
|
||||
generateStateMachineForNamedFunction(
|
||||
@@ -104,7 +104,7 @@ open class FunctionCodegen(
|
||||
return signature
|
||||
}
|
||||
|
||||
private fun IrFunction.isDefault(): Boolean =
|
||||
private fun IrFunction.isKnownToBeTailCall(): Boolean =
|
||||
origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
||||
|
||||
private fun calculateMethodFlags(isStatic: Boolean): Int {
|
||||
|
||||
+5
-1
@@ -88,8 +88,12 @@ fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue {
|
||||
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
|
||||
val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline
|
||||
|
||||
// Boxing and unboxing kotlin.Result leads to CCE in generated code
|
||||
val doNotCoerceKotlinResultInContinuation =
|
||||
codegen.irFunction.isInvokeSuspendOfContinuation(codegen.context) && (irType.isKotlinResult() || irTarget.isKotlinResult())
|
||||
|
||||
// Coerce inline classes
|
||||
if (isFromTypeInlineClass || isToTypeInlineClass) {
|
||||
if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) {
|
||||
val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type
|
||||
val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target
|
||||
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem
|
||||
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
|
||||
name = source.accessorName()
|
||||
visibility = Visibilities.PUBLIC
|
||||
isSuspend = this@makeSimpleFunctionAccessor.isSuspend // synthetic accessors of suspend functions are handled in codegen
|
||||
isSuspend = source.isSuspend // synthetic accessors of suspend functions are handled in codegen
|
||||
}.also { accessor ->
|
||||
// Find the right container to insert the accessor. Simply put, when we call a function on a class A,
|
||||
// we also need to put its accessor into A. However, due to the way that calls are implemented in the
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_REFLECT
|
||||
// WITH_COROUTINES
|
||||
@@ -31,5 +29,5 @@ fun box(): String {
|
||||
result = A<String>().bar()
|
||||
}
|
||||
|
||||
return if (result == "Continuation at test.A.bar(coroutineToString.kt:16)") "OK" else "Fail: $result"
|
||||
return if (result == "Continuation at test.A.bar(coroutineToString.kt:14)") "OK" else "Fail: $result"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// WITH_COROUTINES
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// FULL_JDK
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
Reference in New Issue
Block a user