diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index db7fc14a086..0364d4cc18f 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -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> = - 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> = symbols.primitiveArraysToPrimitiveTypes.flatMap { (array, primitiveType) -> arrayMethods(primitiveType.symbol, array) } + @@ -234,12 +237,5 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) { ): Pair = 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()) - } - - } } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 775e02f6c35..a69b3e5f040 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -444,6 +444,7 @@ private val jvmFilePhases = listOf( kotlinNothingValueExceptionPhase, makePropertyDelegateMethodsStaticPhase, addSuperQualifierToJavaFieldAccessPhase, + replaceNumberToCharCallSitesPhase, renameFieldsPhase, fakeInliningLocalVariablesLowering, diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReplaceNumberToCharCallSitesLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReplaceNumberToCharCallSitesLowering.kt new file mode 100644 index 00000000000..c205c5f181d --- /dev/null +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReplaceNumberToCharCallSitesLowering.kt @@ -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")) + } +} diff --git a/compiler/testData/codegen/box/primitiveTypes/numberToChar/javaSubclass.kt b/compiler/testData/codegen/box/primitiveTypes/numberToChar/javaSubclass.kt index 2135e9601d3..eb665142a57 100644 --- a/compiler/testData/codegen/box/primitiveTypes/numberToChar/javaSubclass.kt +++ b/compiler/testData/codegen/box/primitiveTypes/numberToChar/javaSubclass.kt @@ -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 {