JVM IR: replace unoverridden Number.toChar with toInt().toChar()

This fixes failing tests for JVM_IR in
`box/primitiveTypes/numberToChar`.

 #KT-23447 Fixed
 #KT-46465
This commit is contained in:
Alexander Udalov
2023-03-02 23:00:03 +01:00
committed by Space Team
parent a962ec4553
commit e56e058b7b
4 changed files with 70 additions and 9 deletions
@@ -162,8 +162,11 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
private fun binaryOp(methodName: String, opcode: Int) = binaryFunForPrimitivesAcrossPrimitives(methodName, BinaryOp(opcode))
private fun numberConversionMethods(): List<Pair<Key, IntrinsicMethod>> =
PrimitiveType.NUMBER_TYPES.flatMap { type -> numberConversionMethods(type.symbol) } +
numberConversionMethods(irBuiltIns.numberClass)
PrimitiveType.NUMBER_TYPES.flatMap { type ->
OperatorConventions.NUMBER_CONVERSIONS.map { method ->
createKeyMapping(NumberCast, type.symbol, method.asString())
}
}
private fun arrayMethods(): List<Pair<Key, IntrinsicMethod>> =
symbols.primitiveArraysToPrimitiveTypes.flatMap { (array, primitiveType) -> arrayMethods(primitiveType.symbol, array) } +
@@ -234,12 +237,5 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
): Pair<Key, IntrinsicMethod> =
Key(klass.owner.fqNameWhenAvailable!!, null, name, args.map { getParameterFqName(it) }) to
intrinsic
private fun numberConversionMethods(numberClass: IrClassSymbol) =
OperatorConventions.NUMBER_CONVERSIONS.map { method ->
createKeyMapping(NumberCast, numberClass, method.asString())
}
}
}
@@ -444,6 +444,7 @@ private val jvmFilePhases = listOf(
kotlinNothingValueExceptionPhase,
makePropertyDelegateMethodsStaticPhase,
addSuperQualifierToJavaFieldAccessPhase,
replaceNumberToCharCallSitesPhase,
renameFieldsPhase,
fakeInliningLocalVariablesLowering,
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2023 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.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.functionByName
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.types.isNumber
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.expressions.OperatorConventions
internal val replaceNumberToCharCallSitesPhase = makeIrFilePhase(
::ReplaceNumberToCharCallSitesLowering,
name = "ReplaceNumberToCharCallSites",
description = "Replace `Number.toChar` call sites with `toInt().toChar()`",
)
// This lowering replaces call sites of the form `x.toChar()` to `x.toInt().toChar()`, but only if the declaration of `toChar`
// in the scope of `x` comes from the library class `kotlin.Number`. For example, if `x` is some `MyNumber` which overrides
// `toChar`, we won't replace it because there might be some custom logic, different from `toInt().toChar()`.
//
// This allows us to migrate usages of deprecated `Number.toChar` less painfully in order to remove it in the future (KT-56822).
// Also, this allows to invoke `toChar` on `Number` subclasses declared in Java, which do not have it declared, even though the
// compiler sees it there because `java.lang.Number` is mapped to `kotlin.Number`.
class ReplaceNumberToCharCallSitesLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val callee = expression.symbol.owner
if (callee.name != OperatorConventions.CHAR) return expression
val declaration = callee.resolveFakeOverride() ?: callee
val declaringClassType = declaration.dispatchReceiverParameter?.type ?: return expression
if (!declaringClassType.isNumber()) return expression
val dispatchReceiver = expression.dispatchReceiver ?: return expression
expression.dispatchReceiver = IrCallImpl(
dispatchReceiver.startOffset, dispatchReceiver.endOffset,
context.irBuiltIns.intType, context.irBuiltIns.numberClass.functionByName("toInt"), 0, 0,
).also { toInt ->
toInt.dispatchReceiver = dispatchReceiver
}
return irCall(expression, context.irBuiltIns.intClass.functionByName("toChar"))
}
}
@@ -2,6 +2,9 @@
// ISSUE: KT-23447
// WITH_STDLIB
// IGNORE_BACKEND_K2: JVM_IR
// Ignore reason: KT-57217
// FILE: MyNumber.java
public class MyNumber extends Number {