JVM_IR: IC: Unbox inline class argument of callable reference

if it is unbound and the underlying type is reference type.
If the underlying type is primitive, it is boxed and unboxed
correctly, otherwise, it is simply casted and not unboxed.
Additionally, generate functions for inliner with inline classes
in signature, so unboxing works.
The unboxing is removed after inlining.
 #KT-44722 Fixed
This commit is contained in:
Ilmir Usmanov
2021-02-16 09:24:49 +01:00
parent 744a0fcd25
commit 741c1a864f
19 changed files with 604 additions and 9 deletions
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isFromJava
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isInlineCallableReference
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isMappedToPrimitive
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
@@ -625,14 +627,27 @@ class ExpressionCodegen(
val type = frameMap.typeOf(expression.symbol)
mv.load(findLocalIndex(expression.symbol), type)
unboxResultIfNeeded(expression)
unboxInlineClassArgumentOfInlineCallableReference(expression)
return MaterialValue(this, type, expression.type)
}
// JVM_IR generates inline callable differently from the old backend:
// it generates them as normal functions and not objects.
// Thus, we need to unbox inline class argument with reference underlying type.
private fun unboxInlineClassArgumentOfInlineCallableReference(arg: IrGetValue) {
if (!arg.type.isInlined()) return
if (arg.type.isMappedToPrimitive) return
if (!irFunction.isInlineCallableReference) return
if (irFunction.extensionReceiverParameter?.symbol == arg.symbol) return
StackValue.unboxInlineClass(OBJECT_TYPE, arg.type.erasedUpperBound.defaultType.toIrBasedKotlinType(), mv)
}
// We do not mangle functions if Result is the only parameter of the function,
// thus, if the function overrides generic parameter, its argument is boxed and there is no
// bridge to unbox it. Instead, we unbox it in the non-mangled function manually.
private fun unboxResultIfNeeded(arg: IrGetValue) {
if (arg.type.erasedUpperBound.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME) return
// Do not unbox arguments of lambda, but unbox arguments of callable references
if (irFunction.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return
if (!onlyResultInlineClassParameters()) return
if (irFunction !is IrSimpleFunction) return
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isInlineCallableReference
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.isMappedToPrimitive
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.builtins.StandardNames
@@ -60,7 +62,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
fun mapFieldSignature(field: IrField): String? {
val sw = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)
if (field.correspondingPropertySymbol?.owner?.isVar == true) {
writeParameterType(sw, field.type, field)
writeParameterType(sw, field.type, field, false)
} else {
mapReturnType(field, field.type, sw)
}
@@ -219,6 +221,13 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER &&
function.name.asString() == "box-impl"
private fun forceBoxedInlineClassParametersForInliner(function: IrDeclaration, type: IrType, isBoundReceiver: Boolean): Boolean {
if (isBoundReceiver) return false
if (function !is IrSimpleFunction) return false
if (!function.isInlineCallableReference) return false
return type.isInlined() && !type.isMappedToPrimitive
}
fun mapSignatureSkipGeneric(function: IrFunction): JvmMethodSignature =
mapSignature(function, true)
@@ -243,7 +252,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
val receiverParameter = function.extensionReceiverParameter
if (receiverParameter != null) {
writeParameter(sw, JvmMethodParameterKind.RECEIVER, receiverParameter.type, function)
writeParameter(sw, JvmMethodParameterKind.RECEIVER, receiverParameter.type, function, true)
}
for (parameter in function.valueParameters) {
@@ -256,7 +265,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
if (shouldBoxSingleValueParameterForSpecialCaseOfRemove(function))
parameter.type.makeNullable()
else parameter.type
writeParameter(sw, kind, type, function)
writeParameter(sw, kind, type, function, parameter.symbol == function.extensionReceiverParameter?.symbol)
}
sw.writeReturnType()
@@ -318,15 +327,23 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
return irFunction.allOverridden(false).any { it.parent.kotlinFqName == StandardNames.FqNames.mutableCollection }
}
private fun writeParameter(sw: JvmSignatureWriter, kind: JvmMethodParameterKind, type: IrType, function: IrFunction) {
private fun writeParameter(
sw: JvmSignatureWriter,
kind: JvmMethodParameterKind,
type: IrType,
function: IrFunction,
isReceiver: Boolean
) {
sw.writeParameterType(kind)
writeParameterType(sw, type, function)
writeParameterType(sw, type, function, isReceiver)
sw.writeParameterTypeEnd()
}
private fun writeParameterType(sw: JvmSignatureWriter, type: IrType, declaration: IrDeclaration) {
private fun writeParameterType(sw: JvmSignatureWriter, type: IrType, declaration: IrDeclaration, isReceiver: Boolean) {
if (sw.skipGenericSignature()) {
if (type.isInlined() && declaration.isFromJava()) {
if (type.isInlined() &&
(declaration.isFromJava() || forceBoxedInlineClassParametersForInliner(declaration, type, isReceiver))
) {
typeMapper.mapType(type, TypeMappingMode.GENERIC_ARGUMENT, sw)
} else {
typeMapper.mapType(type, TypeMappingMode.DEFAULT, sw)
@@ -166,3 +166,11 @@ val IrFunction.isInlineClassFieldGetter: Boolean
val IrFunction.isPrimaryInlineClassConstructor: Boolean
get() = this is IrConstructor && isPrimary && constructedClass.isInline
val IrFunction.isInlineCallableReference: Boolean
get() = origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA && name.asString().contains("\$stub_for_inlining")
val IrType.isMappedToPrimitive: Boolean
get() = isInlined() &&
!(isNullable() && makeNotNull().unboxInlineClass().isNullable()) &&
makeNotNull().unboxInlineClass().isPrimitiveType()