diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index ce36177dc7714..378ef87901b07 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -32036,6 +32036,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStandardLibraryBuiltInsLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStandardLibraryBuiltInsLowering.kt index 5d3a399f81e9f..73a80287ddd50 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStandardLibraryBuiltInsLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStandardLibraryBuiltInsLowering.kt @@ -35,8 +35,8 @@ class JvmStandardLibraryBuiltInsLowering(val context: JvmBackendContext) : FileL val parentClass = expression.symbol.owner.parent.fqNameForIrSerialization.asString() val functionName = expression.symbol.owner.name.asString() - Jvm8builtInReplacements[parentClass to functionName]?.let { - return expression.replaceWithCallTo(it) + jvm8builtInReplacements[parentClass to functionName]?.let { replacement -> + return expression.replaceWithCallTo(replacement) } return expression @@ -46,7 +46,7 @@ class JvmStandardLibraryBuiltInsLowering(val context: JvmBackendContext) : FileL irFile.transformChildren(transformer, null) } - private val Jvm8builtInReplacements = mapOf( + private val jvm8builtInReplacements = mapOf( ("kotlin.UInt" to "compareTo") to context.ir.symbols.compareUnsignedInt, ("kotlin.UInt" to "div") to context.ir.symbols.divideUnsignedInt, ("kotlin.UInt" to "rem") to context.ir.symbols.remainderUnsignedInt, @@ -59,7 +59,8 @@ class JvmStandardLibraryBuiltInsLowering(val context: JvmBackendContext) : FileL // Originals are so far only instance methods, and the replacements are // statics, so we copy dispatch receivers to a value argument if needed. - private fun IrCall.replaceWithCallTo(replacement: IrSimpleFunctionSymbol) = + // If we can't coerce arguments to required types, keep original expression (see below). + private fun IrCall.replaceWithCallTo(replacement: IrSimpleFunctionSymbol): IrCall = IrCallImpl.fromSymbolOwner( startOffset, endOffset, @@ -68,23 +69,30 @@ class JvmStandardLibraryBuiltInsLowering(val context: JvmBackendContext) : FileL ).also { newCall -> var valueArgumentOffset = 0 this.dispatchReceiver?.let { - newCall.putValueArgument(valueArgumentOffset, it.coerceTo(replacement.owner.valueParameters[valueArgumentOffset].type)) + val coercedDispatchReceiver = it.coerceIfPossible(replacement.owner.valueParameters[valueArgumentOffset].type) + ?: return this@replaceWithCallTo + newCall.putValueArgument(valueArgumentOffset, coercedDispatchReceiver) valueArgumentOffset++ } - (0 until valueArgumentsCount).forEach { - newCall.putValueArgument(it + valueArgumentOffset, getValueArgument(it)!!.coerceTo(replacement.owner.valueParameters[it].type)) + for (index in 0 until valueArgumentsCount) { + val coercedValueArgument = getValueArgument(index)!!.coerceIfPossible(replacement.owner.valueParameters[index].type) + ?: return this@replaceWithCallTo + newCall.putValueArgument(index + valueArgumentOffset, coercedValueArgument) } } - private fun IrExpression.coerceTo(target: IrType): IrExpression = - IrCallImpl.fromSymbolOwner( - startOffset, - endOffset, - target, - context.ir.symbols.unsafeCoerceIntrinsic - ).also { call -> - call.putTypeArgument(0, type) - call.putTypeArgument(1, target) - call.putValueArgument(0, this) - } + private fun IrExpression.coerceIfPossible(toType: IrType): IrExpression? { + // TODO maybe UnsafeCoerce could handle types with different, but coercible underlying representations. + // See KT-43286 and related tests for details. + val fromJvmType = context.typeMapper.mapType(type) + val toJvmType = context.typeMapper.mapType(toType) + return if (fromJvmType != toJvmType) + null + else + IrCallImpl.fromSymbolOwner(startOffset, endOffset, toType, context.ir.symbols.unsafeCoerceIntrinsic).also { call -> + call.putTypeArgument(0, type) + call.putTypeArgument(1, toType) + call.putValueArgument(0, this) + } + } } diff --git a/compiler/testData/codegen/box/unsignedTypes/kt43286.kt b/compiler/testData/codegen/box/unsignedTypes/kt43286.kt new file mode 100644 index 0000000000000..ac571b4592a80 --- /dev/null +++ b/compiler/testData/codegen/box/unsignedTypes/kt43286.kt @@ -0,0 +1,26 @@ +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// KJS_WITH_FULL_RUNTIME + +class D(val x: UInt?) + +class E(val x: Any) + +fun f(d: D): String { + return d.x?.let { d.x.toString() } ?: "" +} + +fun g(e: E): String { + if (e.x is UInt) return e.x.toString() + return "" +} + +fun box(): String { + val test1 = f(D(42u)) + if (test1 != "42") throw Exception(test1) + + val test2 = g(E(42u)) + if (test2 != "42") throw Exception(test2) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/unsignedTypes/kt43286a.kt b/compiler/testData/codegen/box/unsignedTypes/kt43286a.kt new file mode 100644 index 0000000000000..917ec50c25ecc --- /dev/null +++ b/compiler/testData/codegen/box/unsignedTypes/kt43286a.kt @@ -0,0 +1,8 @@ +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// KJS_WITH_FULL_RUNTIME + +fun box(): String { + val x = 3UL % 2U + return if (x == 1UL) "OK" else "Fail: $x" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 53ecbd37c555c..442ecc7ec94dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -33807,6 +33807,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index b0dd00e812693..beb97d7683d4b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -31441,6 +31441,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index a34cf8c824718..ed92a8e0f669e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -32036,6 +32036,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index fc028038110f4..ea0a206b712cb 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -26002,6 +26002,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 330d600d2da4e..76205ae797395 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -26002,6 +26002,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 1b86ccfe43b77..622042f2b5755 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -26017,6 +26017,16 @@ public void testKt25784() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/kt25784.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 7e513f3ed55cf..86efc618ee6fd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -14305,6 +14305,16 @@ public void testIterateOverListOfBoxedUnsignedValues() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/iterateOverListOfBoxedUnsignedValues.kt"); } + @TestMetadata("kt43286.kt") + public void testKt43286() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286.kt"); + } + + @TestMetadata("kt43286a.kt") + public void testKt43286a() throws Exception { + runTest("compiler/testData/codegen/box/unsignedTypes/kt43286a.kt"); + } + @TestMetadata("literalEqualsNullableUnsigned.kt") public void testLiteralEqualsNullableUnsigned() throws Exception { runTest("compiler/testData/codegen/box/unsignedTypes/literalEqualsNullableUnsigned.kt");