JVM_IR: expect unboxed return value from suspend default stubs

#KT-47206 Fixed
This commit is contained in:
pyos
2021-06-10 09:51:48 +02:00
committed by TeamCityServer
parent 578fcf2ebf
commit cf660cf24a
12 changed files with 133 additions and 30 deletions
@@ -191,17 +191,14 @@ internal fun createFakeContinuation(context: JvmBackendContext): IrExpression =
)
internal fun IrFunction.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(): IrType? {
if (!isSuspend) return null
// Check whether we in fact return inline class
val unboxedReturnType = InlineClassAbi.unboxType(returnType.makeNotNull()) ?: return null
// Force boxing for primitives. NOTE: this also forbids unboxing a nullable inline class into a nullable primitive.
if (unboxedReturnType.isPrimitiveType()) return null
// Force boxing for nullable inline class types with nullable underlying type
if (returnType.isNullable() && unboxedReturnType.isNullable()) return null
// Force boxing if the function overrides function with different type modulo nullability ignoring type parameters
if ((this as? IrSimpleFunction)?.overridesReturningDifferentType(returnType) != false) return null
// Don't box other inline classes
return returnType
if (this !is IrSimpleFunction || !isSuspend) return null
// Unlike `suspendFunctionOriginal()`, this also maps `$default` stubs to the original function.
val original = attributeOwnerId as IrSimpleFunction
val unboxedReturnType = InlineClassAbi.unboxType(original.returnType) ?: return null
// 1. Can't unbox into a primitive, since suspend functions have to return a reference type.
// 2. Force boxing if the function overrides function with different type modulo nullability ignoring type parameters
if (unboxedReturnType.isPrimitiveType() || original.overridesReturningDifferentType(original.returnType)) return null
return original.returnType
}
private fun IrSimpleFunction.overridesReturningDifferentType(returnType: IrType): Boolean {
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.backend.jvm.lower.isMultifileBridge
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.*
@@ -488,8 +487,7 @@ class ExpressionCodegen(
callGenerator.genCall(callable, this, expression, isInsideCondition)
val unboxedInlineClassIrType =
callee.suspendFunctionOriginal().originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
val unboxedInlineClassIrType = callee.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
@@ -519,21 +517,15 @@ class ExpressionCodegen(
wrapJavaClassesIntoKClasses(mv)
MaterialValue(this, AsmTypes.K_CLASS_ARRAY_TYPE, expression.type)
}
unboxedInlineClassIrType != null && !irFunction.isNonBoxingSuspendDelegation() -> {
if (!irFunction.shouldContainSuspendMarkers()) {
// Since the coroutine transformer won't run, we need to do this manually.
mv.generateCoroutineSuspendedCheck(state.languageVersionSettings)
unboxedInlineClassIrType != null && !irFunction.isNonBoxingSuspendDelegation() ->
MaterialValue(this, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType).apply {
if (!irFunction.shouldContainSuspendMarkers()) {
// Since the coroutine transformer won't run, we need to do this manually.
mv.generateCoroutineSuspendedCheck(state.languageVersionSettings)
}
mv.checkcast(type)
}
mv.checkcast(unboxedInlineClassIrType.asmType)
if (irFunction.isInvokeSuspendOfContinuation()) {
// TODO: why is simply materializing the value with type `Object` not enough? This branch shouldn't be needed.
StackValue.boxInlineClass(unboxedInlineClassIrType, mv, typeMapper)
MaterialValue(this, callable.asmMethod.returnType, callable.returnType)
} else {
MaterialValue(this, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType)
}
}
expression.symbol.owner.resultIsActuallyAny(null) == true ->
callee.resultIsActuallyAny(null) == true ->
MaterialValue(this, callable.asmMethod.returnType, context.irBuiltIns.anyNType)
else ->
MaterialValue(this, callable.asmMethod.returnType, callable.returnType)
@@ -907,7 +899,7 @@ class ExpressionCodegen(
}
private fun IrFunction.returnAsmAndIrTypes(): Pair<Type, IrType> {
val unboxedInlineClass = suspendFunctionOriginal().originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
val unboxedInlineClass = originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
// In case of non-boxing delegation, the return type of the tail call was considered to be `Object`,
// so that's also what we'll return here to avoid casts/unboxings/etc.
if (unboxedInlineClass != null && !isNonBoxingSuspendDelegation()) {
@@ -179,7 +179,7 @@ class IrExpressionLambdaImpl(
val freeAsmParameters = asmMethod.argumentTypes.let { it.take(startCapture) + it.drop(endCapture) }
// The return type, on the other hand, should be the original type if this is a suspend lambda that returns
// an unboxed inline class value so that the inliner will box it (FunctionN.invoke should return a boxed value).
val unboxedReturnType = function.suspendFunctionOriginal().originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
val unboxedReturnType = function.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
val unboxedAsmReturnType = unboxedReturnType?.let(codegen.typeMapper::mapType)
invokeMethod = Method(asmMethod.name, unboxedAsmReturnType ?: asmMethod.returnType, freeAsmParameters.toTypedArray())
invokeMethodParameters = freeParameters.map { it.type.toIrBasedKotlinType() }