JVM IR: fix operand type for CompareTo intrinsic

It's incorrect to take the first parameter type from the expression
itself because it can be nullable if smart casts are used. And if it's
nullable, it's mapped to the wrapper type and calling
`comparisonOperandType` for it makes no sense. Instead, take the type
from the callee function, as it's guaranteed to be mapped to a JVM
primitive type.

E.g. in `test1` function in the added test, the problem was that the
dispatch receiver type of the call expression is `Double?`, which is
mapped to `java/lang/Double`, whereas we clearly wanted to obtain the
primitive `D` (double) type.

 #KT-52163 Fixed
This commit is contained in:
Alexander Udalov
2022-04-28 23:01:59 +02:00
parent f8ef028da3
commit 6d664bcd10
14 changed files with 151 additions and 12 deletions
@@ -275,6 +275,15 @@ public class AsmUtil {
}
public static Type comparisonOperandType(Type left, Type right) {
if (!isPrimitive(left) || !isPrimitive(right)) {
throw new IllegalArgumentException("Cannot compute comparison operand type: " + left + ", " + right);
}
if (left == Type.BOOLEAN_TYPE || right == Type.BOOLEAN_TYPE) {
if (left != Type.BOOLEAN_TYPE || right != Type.BOOLEAN_TYPE) {
throw new IllegalArgumentException("Cannot compare boolean with non-boolean: " + left + ", " + right);
}
return Type.BOOLEAN_TYPE;
}
if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
@@ -19,16 +19,23 @@ package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.CallableMethod
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class CompareTo : IntrinsicMethod() {
class CompareTo(private val jvmTarget: JvmTarget) : IntrinsicMethod() {
private fun genInvoke(type: Type?, v: InstructionAdapter) {
when (type) {
Type.INT_TYPE, Type.CHAR_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(II)I", false)
Type.LONG_TYPE -> v.lcmp()
Type.FLOAT_TYPE -> v.invokestatic("java/lang/Float", "compare", "(FF)I", false)
Type.DOUBLE_TYPE -> v.invokestatic("java/lang/Double", "compare", "(DD)I", false)
Type.BOOLEAN_TYPE -> {
check(jvmTarget >= JvmTarget.JVM_1_8) {
"Cannot generate boolean comparison for JVM target 1.6"
}
v.invokestatic("java/lang/Boolean", "compare", "(ZZ)I", false)
}
else -> throw UnsupportedOperationException()
}
}
@@ -149,7 +149,7 @@ public class IntrinsicMethods {
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, null, "arrayOfNulls", 1, new NewArray());
for (PrimitiveType type : PrimitiveType.values()) {
declareIntrinsicFunction(type.getTypeFqName(), "compareTo", 1, new CompareTo());
declareIntrinsicFunction(type.getTypeFqName(), "compareTo", 1, new CompareTo(jvmTarget));
declareIntrinsicFunction(COLLECTIONS_PACKAGE_FQ_NAME.child(Name.identifier(type.getTypeName().asString() + "Iterator")), "next", 0, ITERATOR_NEXT);
}
@@ -25615,6 +25615,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt5937.kt")
public void testKt5937() throws Exception {
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
import org.jetbrains.kotlin.codegen.BranchedValue
import org.jetbrains.kotlin.codegen.NumberCompare
import org.jetbrains.kotlin.codegen.ObjectCompare
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.lexer.KtSingleValueToken
@@ -39,18 +40,21 @@ import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
object CompareTo : IntrinsicMethod() {
private fun genInvoke(type: Type?, v: InstructionAdapter) {
private fun genInvoke(type: Type?, v: InstructionAdapter, context: JvmBackendContext) {
when (type) {
Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE ->
v.invokestatic(
JvmSymbols.INTRINSICS_CLASS_NAME,
"compare",
"(II)I",
false
)
v.invokestatic(JvmSymbols.INTRINSICS_CLASS_NAME, "compare", "(II)I", false)
Type.LONG_TYPE -> v.invokestatic(JvmSymbols.INTRINSICS_CLASS_NAME, "compare", "(JJ)I", false)
Type.FLOAT_TYPE -> v.invokestatic("java/lang/Float", "compare", "(FF)I", false)
Type.DOUBLE_TYPE -> v.invokestatic("java/lang/Double", "compare", "(DD)I", false)
Type.BOOLEAN_TYPE -> {
// We could support it for JVM target 1.6, but it's prohibited now anyway (except for stdlib, which doesn't have such code),
// so throwing an exception instead.
check(context.state.target >= JvmTarget.JVM_1_8) {
"Cannot generate boolean comparison for JVM target 1.6"
}
v.invokestatic("java/lang/Boolean", "compare", "(ZZ)I", false)
}
else -> throw UnsupportedOperationException()
}
}
@@ -60,12 +64,14 @@ object CompareTo : IntrinsicMethod() {
signature: JvmMethodSignature,
context: JvmBackendContext
): IrIntrinsicFunction {
val callee = expression.symbol.owner
val calleeParameter = callee.dispatchReceiverParameter ?: callee.extensionReceiverParameter!!
val parameterType = comparisonOperandType(
expressionType(expression.dispatchReceiver ?: expression.extensionReceiver!!, context),
signature.valueParameters.single().asmType
context.typeMapper.mapType(calleeParameter.type),
signature.valueParameters.single().asmType,
)
return IrIntrinsicFunction.create(expression, signature, context, listOf(parameterType, parameterType)) {
genInvoke(parameterType, it)
genInvoke(parameterType, it, context)
}
}
}
@@ -0,0 +1,8 @@
fun test(): Int {
val d: Any?
d = true
return d.compareTo(false)
}
fun box(): String =
if (test() == 1) "OK" else "Fail"
@@ -0,0 +1,17 @@
fun test1(): Int {
val d: Double?
d = 8.3
return d.compareTo(8)
}
fun test2(): Int {
val d: Double
d = 8.3
return d.compareTo(8)
}
fun box(): String {
if (test1() != 1) return "Fail test1"
if (test2() != 1) return "Fail test2"
return "OK"
}
@@ -25165,6 +25165,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt5937.kt")
public void testKt5937() throws Exception {
@@ -25615,6 +25615,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt5937.kt")
public void testKt5937() throws Exception {
@@ -21170,6 +21170,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@TestMetadata("kt5937.kt")
public void testKt5937() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt5937.kt");
@@ -20253,6 +20253,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt8666.kt")
public void testKt8666() throws Exception {
@@ -20217,6 +20217,18 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt8666.kt")
public void testKt8666() throws Exception {
@@ -17898,6 +17898,16 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@TestMetadata("kt8666.kt")
public void testKt8666() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt8666.kt");
@@ -22864,6 +22864,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/intrinsics/kt12125_inc_2.kt");
}
@Test
@TestMetadata("kt52163_boolean.kt")
public void testKt52163_boolean() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_boolean.kt");
}
@Test
@TestMetadata("kt52163_doubleCompareToInt.kt")
public void testKt52163_doubleCompareToInt() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/kt52163_doubleCompareToInt.kt");
}
@Test
@TestMetadata("kt8666.kt")
public void testKt8666() throws Exception {