diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index f53ed8c3efe..3ed5d0aa49f 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -311,15 +311,6 @@ public class AsmUtil { mv.visitInsn(L2I); } - static StackValue compareExpressionsOnStack(InstructionAdapter v, IElementType opToken, Type operandType) { - if (operandType.getSort() == Type.OBJECT) { - v.invokeinterface("java/lang/Comparable", "compareTo", "(Ljava/lang/Object;)I"); - v.iconst(0); - operandType = Type.INT_TYPE; - } - return StackValue.cmp(opToken, operandType); - } - static StackValue genNullSafeEquals( InstructionAdapter v, IElementType opToken, @@ -394,7 +385,7 @@ public class AsmUtil { boolean rightNullable ) { if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) { - return compareExpressionsOnStack(v, opToken, leftType); + return StackValue.cmp(opToken, leftType); } else { if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 6a99327265d..ca0205d3c71 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -2311,7 +2311,7 @@ public class ExpressionCodegen extends JetVisitor implem } else if (opToken == JetTokens.LT || opToken == JetTokens.LTEQ || opToken == JetTokens.GT || opToken == JetTokens.GTEQ) { - return generateCompareOp(expression.getLeft(), expression.getRight(), opToken, expressionType(expression.getLeft())); + return generateCompareTo(expression); } else if (opToken == JetTokens.ELVIS) { return generateElvis(expression); @@ -2519,10 +2519,25 @@ public class ExpressionCodegen extends JetVisitor implem return StackValue.onStack(exprType); } - private StackValue generateCompareOp(JetExpression left, JetExpression right, IElementType opToken, Type operandType) { - gen(left, operandType); - gen(right, operandType); - return compareExpressionsOnStack(v, opToken, operandType); + private StackValue generateCompareTo(JetBinaryExpression expression) { + DeclarationDescriptor target = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.getOperationReference()); + assert target instanceof FunctionDescriptor : "compareTo target should be a function: " + target; + FunctionDescriptor descriptor = (FunctionDescriptor) target; + + StackValue receiver = gen(expression.getLeft()); + Callable callable = resolveToCallable(descriptor, false); + + StackValue result; + if (callable instanceof IntrinsicMethod) { + result = ((IntrinsicMethod) callable).generate( + this, v, Type.INT_TYPE, expression, Collections.singletonList(expression.getRight()), receiver, state); + } else { + result = invokeOperation(expression, descriptor, (CallableMethod) callable); + } + + result.put(Type.INT_TYPE, v); + v.iconst(0); + return StackValue.cmp(expression.getOperationToken(), Type.INT_TYPE); } private StackValue generateAssignmentExpression(JetBinaryExpression expression) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/CompareTo.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/CompareTo.java index 96f38c22064..16b08af71ee 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/CompareTo.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/CompareTo.java @@ -28,6 +28,8 @@ import org.jetbrains.jet.lang.psi.JetExpression; import java.util.List; +import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; + /** * @author alex.tkachman */ @@ -43,21 +45,25 @@ public class CompareTo implements IntrinsicMethod { @NotNull GenerationState state ) { assert arguments != null; - receiver.put(receiver.type, v); - codegen.gen(arguments.get(0), receiver.type); - if (receiver.type == Type.BYTE_TYPE || receiver.type == Type.SHORT_TYPE || receiver.type == Type.CHAR_TYPE) { + JetExpression argument = arguments.get(0); + Type type = comparisonOperandType(receiver.type, codegen.expressionType(argument)); + + receiver.put(type, v); + codegen.gen(argument, type); + + if (type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || type == Type.CHAR_TYPE) { v.sub(Type.INT_TYPE); } - else if (receiver.type == Type.INT_TYPE) { + else if (type == Type.INT_TYPE) { v.invokestatic("jet/runtime/Intrinsics", "compare", "(II)I"); } - else if (receiver.type == Type.LONG_TYPE) { + else if (type == Type.LONG_TYPE) { v.invokestatic("jet/runtime/Intrinsics", "compare", "(JJ)I"); } - else if (receiver.type == Type.FLOAT_TYPE) { + else if (type == Type.FLOAT_TYPE) { v.invokestatic("java/lang/Float", "compare", "(FF)I"); } - else if (receiver.type == Type.DOUBLE_TYPE) { + else if (type == Type.DOUBLE_TYPE) { v.invokestatic("java/lang/Double", "compare", "(DD)I"); } else { @@ -65,4 +71,11 @@ public class CompareTo implements IntrinsicMethod { } return StackValue.onStack(Type.INT_TYPE); } + + private static Type comparisonOperandType(Type left, Type right) { + 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; + return Type.INT_TYPE; + } } diff --git a/compiler/testData/codegen/operatorConventions/compareTo/comparable.kt b/compiler/testData/codegen/operatorConventions/compareTo/comparable.kt new file mode 100644 index 00000000000..fec5ac5bfa0 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/comparable.kt @@ -0,0 +1,16 @@ +trait A : Comparable + +class B(val x: Int) : A { + override fun compareTo(other: A) = x.compareTo((other as B).x) +} + +fun checkLess(x: A, y: A) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(B(0), B(1)) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/doubleInt.kt b/compiler/testData/codegen/operatorConventions/compareTo/doubleInt.kt new file mode 100644 index 00000000000..9818affbfba --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/doubleInt.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Double, y: Int) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(0.5, 1) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/doubleLong.kt b/compiler/testData/codegen/operatorConventions/compareTo/doubleLong.kt new file mode 100644 index 00000000000..294560659ec --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/doubleLong.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Double, y: Long) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(0.5, 1.toLong()) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/extensionArray.kt b/compiler/testData/codegen/operatorConventions/compareTo/extensionArray.kt new file mode 100644 index 00000000000..d25388d1dd4 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/extensionArray.kt @@ -0,0 +1,16 @@ +fun checkLess(x: Array, y: Array) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun Array.compareTo(other: Array) = size - other.size + +fun box(): String { + val a = Array(0, {0}) + val b = Array(1, {0}) + return checkLess(a, b) +} diff --git a/compiler/testData/codegen/operatorConventions/compareTo/extensionObject.kt b/compiler/testData/codegen/operatorConventions/compareTo/extensionObject.kt new file mode 100644 index 00000000000..3dc1a6fca87 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/extensionObject.kt @@ -0,0 +1,14 @@ +class A(val x: Int) + +fun A.compareTo(other: A) = x.compareTo(other.x) + +fun checkLess(x: A, y: A) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(A(0), A(1)) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/intDouble.kt b/compiler/testData/codegen/operatorConventions/compareTo/intDouble.kt new file mode 100644 index 00000000000..a76709fe2db --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/intDouble.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Int, y: Double) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(0, 0.5) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/intLong.kt b/compiler/testData/codegen/operatorConventions/compareTo/intLong.kt new file mode 100644 index 00000000000..dc91a1d0d51 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/intLong.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Int, y: Long) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(0, 123456789123.toLong()) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/longDouble.kt b/compiler/testData/codegen/operatorConventions/compareTo/longDouble.kt new file mode 100644 index 00000000000..90e43d49215 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/longDouble.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Long, y: Double) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(0.toLong(), 0.5) diff --git a/compiler/testData/codegen/operatorConventions/compareTo/longInt.kt b/compiler/testData/codegen/operatorConventions/compareTo/longInt.kt new file mode 100644 index 00000000000..623a3a46e01 --- /dev/null +++ b/compiler/testData/codegen/operatorConventions/compareTo/longInt.kt @@ -0,0 +1,10 @@ +fun checkLess(x: Long, y: Int) = when { + x >= y -> "Fail $x >= $y" + !(x < y) -> "Fail !($x < $y)" + !(x <= y) -> "Fail !($x <= $y)" + x > y -> "Fail $x > $y" + x.compareTo(y) >= 0 -> "Fail $x.compareTo($y) >= 0" + else -> "OK" +} + +fun box() = checkLess(-123456789123.toLong(), 0) diff --git a/compiler/testData/codegen/regressions/kt3078.kt b/compiler/testData/codegen/regressions/kt3078.kt new file mode 100644 index 00000000000..cd834c89b2f --- /dev/null +++ b/compiler/testData/codegen/regressions/kt3078.kt @@ -0,0 +1,7 @@ +fun box(): String { + if (1 >= 1.9) return "Fail #1" + if (1.compareTo(1.1) >= 0) return "Fail #2" + if (1.9 <= 1) return "Fail #3" + if (1.1.compareTo(1) <= 0) return "Fail #4" + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/PrimitiveTypesTest.java b/compiler/tests/org/jetbrains/jet/codegen/PrimitiveTypesTest.java index d93fcf50a58..2b79fda7915 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/PrimitiveTypesTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/PrimitiveTypesTest.java @@ -460,4 +460,8 @@ public class PrimitiveTypesTest extends CodegenTestCase { public void testUnboxComparable() { blackBoxFile("primitiveTypes/unboxComparable.kt"); } + + public void testKt3078() { + blackBoxFile("regressions/kt3078.kt"); + } } diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/OperatorConventionsTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/OperatorConventionsTestGenerated.java new file mode 100644 index 00000000000..3300c68bcd5 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/OperatorConventionsTestGenerated.java @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jet.codegen.generated; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.io.File; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.InnerTestClasses; +import org.jetbrains.jet.test.TestMetadata; + +import org.jetbrains.jet.codegen.generated.AbstractCodegenTest; + +/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ +@TestMetadata("compiler/testData/codegen/operatorConventions") +@InnerTestClasses({OperatorConventionsTestGenerated.CompareTo.class}) +public class OperatorConventionsTestGenerated extends AbstractCodegenTest { + public void testAllFilesPresentInOperatorConventions() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/operatorConventions"), "kt", true); + } + + @TestMetadata("compiler/testData/codegen/operatorConventions/compareTo") + public static class CompareTo extends AbstractCodegenTest { + public void testAllFilesPresentInCompareTo() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/operatorConventions/compareTo"), "kt", true); + } + + @TestMetadata("comparable.kt") + public void testComparable() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/comparable.kt"); + } + + @TestMetadata("doubleInt.kt") + public void testDoubleInt() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/doubleInt.kt"); + } + + @TestMetadata("doubleLong.kt") + public void testDoubleLong() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/doubleLong.kt"); + } + + @TestMetadata("extensionArray.kt") + public void testExtensionArray() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/extensionArray.kt"); + } + + @TestMetadata("extensionObject.kt") + public void testExtensionObject() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/extensionObject.kt"); + } + + @TestMetadata("intDouble.kt") + public void testIntDouble() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/intDouble.kt"); + } + + @TestMetadata("intLong.kt") + public void testIntLong() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/intLong.kt"); + } + + @TestMetadata("longDouble.kt") + public void testLongDouble() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/longDouble.kt"); + } + + @TestMetadata("longInt.kt") + public void testLongInt() throws Exception { + doTest("compiler/testData/codegen/operatorConventions/compareTo/longInt.kt"); + } + + } + + public static Test suite() { + TestSuite suite = new TestSuite("OperatorConventionsTestGenerated"); + suite.addTestSuite(OperatorConventionsTestGenerated.class); + suite.addTestSuite(CompareTo.class); + return suite; + } +} diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java index f0ca7c3d223..0e21d3a1bfa 100644 --- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java +++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java @@ -114,6 +114,13 @@ public class GenerateTests { testModel("compiler/testData/codegen/instructions") ); + generateTest( + "compiler/tests/", + "OperatorConventionsTestGenerated", + AbstractCodegenTest.class, + testModel("compiler/testData/codegen/operatorConventions") + ); + generateTest( "compiler/tests/", "ExtensionPropertiesTestGenerated",