KT-27524 Don't box (some) inline classes in suspend fun return

If an inline class is mapped to a reference type (or an array), it's Ok
to treat JVM view on a suspend function as returning a value of
corresponding inline class (although in reality it returns 'Any?'
because of COROUTINE_SUSPENDED).
This commit is contained in:
Dmitry Petrov
2020-03-26 20:37:29 +03:00
parent d8bc29e6c6
commit 042424d599
22 changed files with 1220 additions and 7 deletions
@@ -37,7 +37,7 @@ interface Callable {
fun invokeMethodWithArguments(resolvedCall: ResolvedCall<*>, receiver: StackValue, codegen: ExpressionCodegen): StackValue {
// it's important to use unsubstituted return type here to unbox value if it comes from type variable
return StackValue.functionCall(returnType, resolvedCall.resultingDescriptor.original.returnType) {
return StackValue.functionCall(returnType, returnKotlinType ?: resolvedCall.resultingDescriptor.original.returnType) {
codegen.invokeMethodWithArguments(this, resolvedCall, receiver)
}
}
@@ -1648,7 +1648,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
else {
returnType = this.returnType;
returnKotlinType = this.context.getFunctionDescriptor().getReturnType();
returnKotlinType = typeMapper.getReturnValueType(this.context.getFunctionDescriptor());
}
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : StackValue.none();
@@ -1701,7 +1701,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
//FIRST_FUN_LABEL to prevent clashing with existing labels
return new NonLocalReturnInfo(
new JvmKotlinType(typeMapper.mapReturnType(containingFunction), containingFunction.getReturnType()),
new JvmKotlinType(
typeMapper.mapReturnType(containingFunction),
typeMapper.getReturnValueType(containingFunction)
),
FIRST_FUN_LABEL
);
} else {
@@ -1739,7 +1742,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
: returnType;
KotlinType kotlinTypeForExpression =
isBlockedNamedFunction || isVoidCoroutineLambda ? null : context.getFunctionDescriptor().getReturnType();
isBlockedNamedFunction || isVoidCoroutineLambda
? null
: typeMapper.getReturnValueType(context.getFunctionDescriptor());
gen(expr, typeForExpression, kotlinTypeForExpression);
@@ -362,6 +362,19 @@ class KotlinTypeMapper @JvmOverloads constructor(
}
}
fun getReturnValueType(functionDescriptor: FunctionDescriptor): KotlinType =
getActualReturnTypeForSuspendFunctionWithInlineClassReturnTypeHack(functionDescriptor)
?: functionDescriptor.returnType!!
private fun getActualReturnTypeForSuspendFunctionWithInlineClassReturnTypeHack(functionDescriptor: FunctionDescriptor): KotlinType? {
val originalSuspendFunctionForJvmView = functionDescriptor.unwrapInitialDescriptorForSuspendFunction()
if (originalSuspendFunctionForJvmView === functionDescriptor) return null
val originalReturnType = originalSuspendFunctionForJvmView.returnType!!
if (!originalReturnType.isInlineClassType()) return null
val jvmReturnType = mapType(originalReturnType)
return if (AsmUtil.isPrimitive(jvmReturnType)) null else originalReturnType
}
@JvmOverloads
fun mapToCallableMethod(
descriptor: FunctionDescriptor,
@@ -431,7 +444,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
invokeOpcode = INVOKESTATIC
val originalDescriptor = descriptor.original
signature = mapSignatureSkipGeneric(originalDescriptor, OwnerKind.DEFAULT_IMPLS)
returnKotlinType = originalDescriptor.returnType
returnKotlinType = getReturnValueType(originalDescriptor)
if (descriptor is AccessorForCallableDescriptor<*> && descriptor.calleeDescriptor.hasJvmDefaultAnnotation()) {
owner = mapClass(functionParent)
isInterfaceMember = true
@@ -482,7 +495,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
skipGenericSignature = true,
hasSpecialBridge = false
)
returnKotlinType = functionToCall.returnType
returnKotlinType = getReturnValueType(functionToCall)
val receiver = if (currentIsInterface && !originalIsInterface || functionParent is FunctionClassDescriptor)
declarationOwner
@@ -495,7 +508,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
} else {
val originalDescriptor = functionDescriptor.original
signature = mapSignatureSkipGeneric(originalDescriptor)
returnKotlinType = originalDescriptor.returnType
returnKotlinType = getReturnValueType(originalDescriptor)
owner = mapOwner(functionDescriptor)
ownerForDefaultImpl = owner
baseMethodDescriptor = functionDescriptor