From 47765352051f416123db29696f7c6fd25d586d4c Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 2 Feb 2018 11:49:44 +0300 Subject: [PATCH] Implement IR primitives for number comparisons in JVM BE --- .../backend/jvm/intrinsics/CompareTo.kt | 73 ++++++---------- .../kotlin/backend/jvm/intrinsics/Equals.kt | 87 +++++++++++++++++-- .../jvm/intrinsics/IntrinsicMethods.java | 10 ++- .../jvm/intrinsics/IrIntrinsicMethods.kt | 45 ++++++---- .../comparableToDouble.kt | 15 ++++ .../primitiveNumberComparisons/doubleEqeq.kt | 17 ++++ .../primitiveNumberComparisons/floatEqeq.kt | 17 ++++ .../mixedNumberTypes.kt | 8 ++ .../ir/IrOnlyBoxCodegenTestGenerated.java | 33 +++++++ 9 files changed, 235 insertions(+), 70 deletions(-) create mode 100644 compiler/testData/ir/box/primitiveNumberComparisons/comparableToDouble.kt create mode 100644 compiler/testData/ir/box/primitiveNumberComparisons/doubleEqeq.kt create mode 100644 compiler/testData/ir/box/primitiveNumberComparisons/floatEqeq.kt create mode 100644 compiler/testData/ir/box/primitiveNumberComparisons/mixedNumberTypes.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt index 5f61865253d..3a79af00162 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt @@ -16,20 +16,13 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics -import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo -import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType -import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive -import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.StackValue -import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression -import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin -import org.jetbrains.kotlin.ir.expressions.impl.IrPrimitiveCallBase -import org.jetbrains.kotlin.ir.expressions.impl.IrUnaryPrimitiveImpl -import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import java.lang.UnsupportedOperationException @@ -45,10 +38,14 @@ class CompareTo : IntrinsicMethod() { } } - override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction { + override fun toCallable( + expression: IrMemberAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction { val parameterType = comparisonOperandType( - expressionType(expression.dispatchReceiver ?: expression.extensionReceiver!!, context), - signature.valueParameters.single().asmType + expressionType(expression.dispatchReceiver ?: expression.extensionReceiver!!, context), + signature.valueParameters.single().asmType ) return IrIntrinsicFunction.create(expression, signature, context, listOf(parameterType, parameterType)) { genInvoke(parameterType, it) @@ -57,44 +54,24 @@ class CompareTo : IntrinsicMethod() { } -class IrCompareTo : IntrinsicMethod() { - override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction { - assert(expression is IrPrimitiveCallBase) - val compareCall = expression.getValueArgument(0) as IrCall - val args = compareCall.receiverAndArgs() - val argTypes = args.asmTypes(context) - val leftType = argTypes[0] - val rightType = argTypes[1] - val parameterType = comparisonOperandType(leftType, rightType) +class PrimitiveComparison( + private val primitiveNumberType: KotlinType, + private val operatorToken: KtSingleValueToken +) : IntrinsicMethod() { - val newSignature = context.state.typeMapper.mapSignatureSkipGeneric(compareCall.descriptor, OwnerKind.IMPLEMENTATION) - return object : IrIntrinsicFunction(compareCall, newSignature, context, listOf(parameterType, parameterType)) { - override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue { - val isPrimitiveIntrinsic = codegen.intrinsics.intrinsics.getIntrinsic(compareCall.descriptor) != null - val operationType: Type - val leftValue: StackValue - val rightValue: StackValue - if (isPrimitive(leftType) && isPrimitive(rightType) && isPrimitiveIntrinsic) { - operationType = comparisonOperandType(leftType, rightType) - leftValue = codegen.gen(args[0], operationType, data) - rightValue = codegen.gen(args[1], operationType,data) - } - else { - operationType = Type.INT_TYPE - leftValue = codegen.gen(compareCall, data) - rightValue = StackValue.constant(0, operationType) - } - val origin = expression.origin - val token = when (origin) { - IrStatementOrigin.GT -> KtTokens.GT - IrStatementOrigin.GTEQ -> KtTokens.GTEQ - IrStatementOrigin.LT -> KtTokens.LT - IrStatementOrigin.LTEQ -> KtTokens.LTEQ - else -> TODO() - } - StackValue.cmp(token, operationType, leftValue, rightValue).put(Type.BOOLEAN_TYPE, v) - return StackValue.onStack(Type.BOOLEAN_TYPE) + override fun toCallable( + expression: IrMemberAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction { + val parameterType = context.state.typeMapper.mapType(primitiveNumberType) + + return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) { + override fun genInvokeInstruction(v: InstructionAdapter) { + StackValue.cmp(operatorToken, parameterType, StackValue.onStack(parameterType), StackValue.onStack(parameterType)) + .put(Type.BOOLEAN_TYPE, v) } } } + } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt index a49ab7a127e..ca7c9adaa29 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/Equals.kt @@ -18,17 +18,27 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil.* import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class Equals(val operator: IElementType) : IntrinsicMethod() { - override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction { + + override fun toCallable( + expression: IrMemberAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction { val receiverAndArgs = expression.receiverAndArgs().apply { assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" } } @@ -40,8 +50,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { rightType = boxType(rightType) } - - return object: IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) { + return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) { override fun genInvokeInstruction(v: InstructionAdapter) { val opToken = expression.origin @@ -49,8 +58,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { // TODO: always casting to the type of the left operand in case of primitives looks wrong val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType)) - } - else { + } else { genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType)) } value.put(Type.BOOLEAN_TYPE, v) @@ -58,3 +66,72 @@ class Equals(val operator: IElementType) : IntrinsicMethod() { } } } + + +class Ieee754Equals(val operandType: Type) : IntrinsicMethod() { + + private val boxedOperandType = AsmUtil.boxType(operandType) + + override fun toCallable( + expression: IrMemberAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction { + class Ieee754AreEqual( + val left: Type, + val right: Type + ) : IrIntrinsicFunction(expression, signature, context, listOf(left, right)) { + override fun genInvokeInstruction(v: InstructionAdapter) { + v.invokestatic( + IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", + Type.getMethodDescriptor(Type.BOOLEAN_TYPE, left, right), + false + ) + } + } + + val arg0Type = expression.getValueArgument(0)!!.type + if (!arg0Type.isPrimitiveNumberOrNullableType()) throw AssertionError("Should be primitive or nullable primitive type: $arg0Type") + + val arg1Type = expression.getValueArgument(1)!!.type + if (!arg1Type.isPrimitiveNumberOrNullableType()) throw AssertionError("Should be primitive or nullable primitive type: $arg1Type") + + val arg0isNullable = arg0Type.isMarkedNullable + val arg1isNullable = arg1Type.isMarkedNullable + + return when { + !arg0isNullable && !arg1isNullable -> + object : IrIntrinsicFunction(expression, signature, context, listOf(operandType, operandType)) { + override fun genInvokeInstruction(v: InstructionAdapter) { + StackValue.cmp(KtTokens.EQEQ, operandType, StackValue.onStack(operandType), StackValue.onStack(operandType)) + .put(Type.BOOLEAN_TYPE, v) + } + } + + arg0isNullable && !arg1isNullable -> + Ieee754AreEqual(boxedOperandType, operandType) + + !arg0isNullable && arg1isNullable -> + Ieee754AreEqual(operandType, boxedOperandType) + + else -> + Ieee754AreEqual(boxedOperandType, boxedOperandType) + } + } + +} + +class TotalOrderEquals(operandType: Type) : IntrinsicMethod() { + private val boxedType = AsmUtil.boxType(operandType) + + override fun toCallable( + expression: IrMemberAccessExpression, + signature: JvmMethodSignature, + context: JvmBackendContext + ): IrIntrinsicFunction = + object : IrIntrinsicFunction(expression, signature, context, listOf(boxedType, boxedType)) { + override fun genInvokeInstruction(v: InstructionAdapter) { + v.invokevirtual(boxedType.internalName, "equals", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, AsmTypes.OBJECT_TYPE), false) + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethods.java b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethods.java index 3723820edf3..3e06f232b0f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethods.java +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IntrinsicMethods.java @@ -27,8 +27,10 @@ import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType; import org.jetbrains.kotlin.types.expressions.OperatorConventions; +import org.jetbrains.org.objectweb.asm.Type; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -92,7 +94,13 @@ public class IntrinsicMethods { for (PrimitiveType type : PrimitiveType.values()) { FqName typeFqName = type.getTypeFqName(); - declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS); + Type asmPrimitiveType = AsmTypes.valueTypeForPrimitive(type); + if (asmPrimitiveType == Type.FLOAT_TYPE || asmPrimitiveType == Type.DOUBLE_TYPE) { + declareIntrinsicFunction(typeFqName, "equals", 1, new TotalOrderEquals(asmPrimitiveType)); + } + else { + declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS); + } declareIntrinsicFunction(typeFqName, "hashCode", 0, HASH_CODE); declareIntrinsicFunction(typeFqName, "toString", 0, TO_STRING); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt index 232995ba425..94617175994 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/IrIntrinsicMethods.kt @@ -18,9 +18,13 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.SimpleType +import org.jetbrains.org.objectweb.asm.Type class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) { @@ -28,25 +32,34 @@ class IrIntrinsicMethods(irBuiltIns: IrBuiltIns) { private val irMapping = hashMapOf() + private fun createPrimitiveComparisonIntrinsics(typeToIrFun: Map, operator: KtSingleValueToken) { + for ((type, irFun) in typeToIrFun) { + irMapping[irFun.descriptor] = PrimitiveComparison(type, operator) + } + } + init { - irMapping.put(irBuiltIns.eqeq, Equals(KtTokens.EQEQ)) - irMapping.put(irBuiltIns.eqeqeq, Equals(KtTokens.EQEQEQ)) - irMapping.put(irBuiltIns.booleanNot, Not()) - val compare = IrCompareTo() - irMapping.put(irBuiltIns.lt0, compare) - irMapping.put(irBuiltIns.lteq0, compare) - irMapping.put(irBuiltIns.gt0, compare) - irMapping.put(irBuiltIns.gteq0, compare) - irMapping.put(irBuiltIns.enumValueOf, IrEnumValueOf()) - irMapping.put(irBuiltIns.noWhenBranchMatchedException, IrNoWhenBranchMatchedException()) - irMapping.put(irBuiltIns.throwNpe, ThrowNPE()) + irMapping[irBuiltIns.eqeq] = Equals(KtTokens.EQEQ) + irMapping[irBuiltIns.eqeqeq] = Equals(KtTokens.EQEQEQ) + irMapping[irBuiltIns.ieee754equalsFunByOperandType[irBuiltIns.float]!!.descriptor] = Ieee754Equals(Type.FLOAT_TYPE) + irMapping[irBuiltIns.ieee754equalsFunByOperandType[irBuiltIns.double]!!.descriptor] = Ieee754Equals(Type.DOUBLE_TYPE) + irMapping[irBuiltIns.booleanNot] = Not() + + createPrimitiveComparisonIntrinsics(irBuiltIns.lessFunByOperandType, KtTokens.LT) + createPrimitiveComparisonIntrinsics(irBuiltIns.lessOrEqualFunByOperandType, KtTokens.LTEQ) + createPrimitiveComparisonIntrinsics(irBuiltIns.greaterFunByOperandType, KtTokens.GT) + createPrimitiveComparisonIntrinsics(irBuiltIns.greaterOrEqualFunByOperandType, KtTokens.GTEQ) + + irMapping[irBuiltIns.enumValueOf] = IrEnumValueOf() + irMapping[irBuiltIns.noWhenBranchMatchedException] = IrNoWhenBranchMatchedException() + irMapping[irBuiltIns.throwNpe] = ThrowNPE() } fun getIntrinsic(descriptor: CallableMemberDescriptor): IntrinsicMethod? { - return intrinsics.getIntrinsic(descriptor) ?: - (if (descriptor is PropertyAccessorDescriptor) - intrinsics.getIntrinsic(DescriptorUtils.unwrapFakeOverride(descriptor.correspondingProperty)) - else null) ?: irMapping[descriptor.original] + intrinsics.getIntrinsic(descriptor)?.let { return it } + if (descriptor is PropertyAccessorDescriptor) { + return intrinsics.getIntrinsic(DescriptorUtils.unwrapFakeOverride(descriptor.correspondingProperty)) + } + return irMapping[descriptor.original] } - } diff --git a/compiler/testData/ir/box/primitiveNumberComparisons/comparableToDouble.kt b/compiler/testData/ir/box/primitiveNumberComparisons/comparableToDouble.kt new file mode 100644 index 00000000000..33a684d38f6 --- /dev/null +++ b/compiler/testData/ir/box/primitiveNumberComparisons/comparableToDouble.kt @@ -0,0 +1,15 @@ +fun ltDD(x: Comparable, y: Double) = + x is Double && x < y + +fun ltCD(x: Comparable, y: Double) = + x < y + +fun box(): String { + val Z = 0.0 + val NZ = -0.0 + + if (ltDD(NZ, Z)) return "Fail 1" + if (!ltCD(NZ, Z)) return "Fail 2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/ir/box/primitiveNumberComparisons/doubleEqeq.kt b/compiler/testData/ir/box/primitiveNumberComparisons/doubleEqeq.kt new file mode 100644 index 00000000000..3d506b4c4c9 --- /dev/null +++ b/compiler/testData/ir/box/primitiveNumberComparisons/doubleEqeq.kt @@ -0,0 +1,17 @@ +fun eqeq(x: Any, y: Any) = + x is Double && y is Double && x == y + +fun anyEqeq(x: Any, y: Any) = + x == y + +fun box(): String { + val Z = 0.0 + val NZ = -0.0 + + if (!(Z == NZ)) return "Fail 1" + if (!eqeq(Z, NZ)) return "Fail 2" + + if (anyEqeq(Z, NZ)) return "Fail A" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/ir/box/primitiveNumberComparisons/floatEqeq.kt b/compiler/testData/ir/box/primitiveNumberComparisons/floatEqeq.kt new file mode 100644 index 00000000000..c8579dcbba2 --- /dev/null +++ b/compiler/testData/ir/box/primitiveNumberComparisons/floatEqeq.kt @@ -0,0 +1,17 @@ +fun eqeq(x: Any, y: Any) = + x is Float && y is Float && x == y + +fun anyEqeq(x: Any, y: Any) = + x == y + +fun box(): String { + val Z = 0.0F + val NZ = -0.0F + + if (!(Z == NZ)) return "Fail 1" + if (!eqeq(Z, NZ)) return "Fail 2" + + if (anyEqeq(Z, NZ)) return "Fail A" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/ir/box/primitiveNumberComparisons/mixedNumberTypes.kt b/compiler/testData/ir/box/primitiveNumberComparisons/mixedNumberTypes.kt new file mode 100644 index 00000000000..7c458cacf5d --- /dev/null +++ b/compiler/testData/ir/box/primitiveNumberComparisons/mixedNumberTypes.kt @@ -0,0 +1,8 @@ +fun ltDI(x: Any, y: Any) = + x is Double && y is Int && x < y + +fun box(): String { + if (ltDI(-0.0, 0)) return "Fail 1" + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java index 7f381e1fbd9..11e76cfc3de 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java @@ -123,4 +123,37 @@ public class IrOnlyBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTest doTest(fileName); } } + + @TestMetadata("compiler/testData/ir/box/primitiveNumberComparisons") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PrimitiveNumberComparisons extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInPrimitiveNumberComparisons() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/box/primitiveNumberComparisons"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("comparableToDouble.kt") + public void testComparableToDouble() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/primitiveNumberComparisons/comparableToDouble.kt"); + doTest(fileName); + } + + @TestMetadata("doubleEqeq.kt") + public void testDoubleEqeq() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/primitiveNumberComparisons/doubleEqeq.kt"); + doTest(fileName); + } + + @TestMetadata("floatEqeq.kt") + public void testFloatEqeq() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/primitiveNumberComparisons/floatEqeq.kt"); + doTest(fileName); + } + + @TestMetadata("mixedNumberTypes.kt") + public void testMixedNumberTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/primitiveNumberComparisons/mixedNumberTypes.kt"); + doTest(fileName); + } + } }