From 1edaf43051157db7f85fdad0d6980be0a5e9d326 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 16 Oct 2014 19:09:43 +0400 Subject: [PATCH] Fix Double and Float equality/hashCode in data classes #KT-5818 Fixed --- .../org/jetbrains/jet/codegen/AsmUtil.java | 33 +++++++++---------- .../codegen/ImplementationBodyCodegen.java | 14 ++++++-- .../org/jetbrains/jet/codegen/StackValue.java | 2 +- .../boxWithStdlib/dataClasses/doubleParam.kt | 30 +++++++++++++++++ .../boxWithStdlib/dataClasses/floatParam.kt | 30 +++++++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 12 +++++++ 6 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/dataClasses/doubleParam.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/dataClasses/floatParam.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index b262d843388..8a32a2d8de1 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -486,29 +486,28 @@ public class AsmUtil { v.xor(Type.INT_TYPE); } + @NotNull public static StackValue genEqualsForExpressionsOnStack( - InstructionAdapter v, - IElementType opToken, - Type leftType, - Type rightType + @NotNull InstructionAdapter v, + @NotNull IElementType opToken, + @NotNull Type leftType, + @NotNull Type rightType ) { - if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) { + if (isPrimitive(leftType) && leftType == rightType) { return StackValue.cmp(opToken, leftType); } - else { - if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) { - return StackValue.cmp(opToken, leftType); - } - else { - v.invokestatic("kotlin/jvm/internal/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false); - if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) { - genInvertBoolean(v); - } - - return StackValue.onStack(Type.BOOLEAN_TYPE); - } + if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) { + return StackValue.cmp(opToken, leftType); } + + v.invokestatic("kotlin/jvm/internal/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false); + + if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) { + genInvertBoolean(v); + } + + return StackValue.onStack(Type.BOOLEAN_TYPE); } public static void genIncrement(Type expectedType, int myDelta, InstructionAdapter v) { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 8f61fe75ba2..0d06b35fb2e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -645,15 +645,23 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } else { iv.invokestatic("java/util/Arrays", "equals", - "([" + elementType.getDescriptor() + "[" + elementType.getDescriptor() + ")Z", false); + "(" + asmType.getDescriptor() + asmType.getDescriptor() + ")Z", false); } + iv.ifeq(ne); + } + else if (asmType.getSort() == Type.FLOAT) { + iv.invokestatic("java/lang/Float", "compare", "(FF)I", false); + iv.ifne(ne); + } + else if (asmType.getSort() == Type.DOUBLE) { + iv.invokestatic("java/lang/Double", "compare", "(DD)I", false); + iv.ifne(ne); } else { StackValue value = genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType); value.put(Type.BOOLEAN_TYPE, iv); + iv.ifeq(ne); } - - iv.ifeq(ne); } iv.mark(eq); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java index 1c6ed3673e2..794f997d453 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java @@ -517,7 +517,7 @@ public abstract class StackValue { opcode = jumpIfFalse ? IFGT : IFLE; } else { - throw new UnsupportedOperationException("don't know how to generate this condjump"); + throw new UnsupportedOperationException("Don't know how to generate this condJump: " + opToken); } if (operandType == Type.FLOAT_TYPE || operandType == Type.DOUBLE_TYPE) { if (opToken == JetTokens.GT || opToken == JetTokens.GTEQ) { diff --git a/compiler/testData/codegen/boxWithStdlib/dataClasses/doubleParam.kt b/compiler/testData/codegen/boxWithStdlib/dataClasses/doubleParam.kt new file mode 100644 index 00000000000..3a97e7002a6 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/dataClasses/doubleParam.kt @@ -0,0 +1,30 @@ +import java.util.HashSet + +val NAN = java.lang.Double.NaN + +data class A(val x: Double) + +fun box(): String { + if (A(+0.0) == A(-0.0)) return "Fail: +0.0 == -0.0" + if (A(+0.0).hashCode() == A(-0.0).hashCode()) return "Fail: hash(+0.0) == hash(-0.0)" + + if (A(NAN) != A(NAN)) return "Fail: NaN != NaN" + if (A(NAN).hashCode() != A(NAN).hashCode()) return "Fail: hash(NaN) != hash(NaN)" + + val s = HashSet() + for (times in 1..5) { + s.add(A(3.14)) + s.add(A(+0.0)) + s.add(A(-0.0)) + s.add(A(-2.72)) + s.add(A(NAN)) + } + + if (A(3.14) !in s) return "Fail: 3.14 not found" + if (A(+0.0) !in s) return "Fail: +0.0 not found" + if (A(-0.0) !in s) return "Fail: -0.0 not found" + if (A(-2.72) !in s) return "Fail: -2.72 not found" + if (A(NAN) !in s) return "Fail: NaN not found" + + return if (s.size == 5) "OK" else "Fail $s" +} diff --git a/compiler/testData/codegen/boxWithStdlib/dataClasses/floatParam.kt b/compiler/testData/codegen/boxWithStdlib/dataClasses/floatParam.kt new file mode 100644 index 00000000000..5335df49f67 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/dataClasses/floatParam.kt @@ -0,0 +1,30 @@ +import java.util.HashSet + +val NAN = java.lang.Float.NaN + +data class A(val x: Float) + +fun box(): String { + if (A(+0f) == A(-0f)) return "Fail: +0 == -0" + if (A(+0f).hashCode() == A(-0f).hashCode()) return "Fail: hash(+0) == hash(-0)" + + if (A(NAN) != A(NAN)) return "Fail: NaN != NaN" + if (A(NAN).hashCode() != A(NAN).hashCode()) return "Fail: hash(NaN) != hash(NaN)" + + val s = HashSet() + for (times in 1..5) { + s.add(A(3.14f)) + s.add(A(+0f)) + s.add(A(-0f)) + s.add(A(-2.72f)) + s.add(A(NAN)) + } + + if (A(3.14f) !in s) return "Fail: 3.14 not found" + if (A(+0f) !in s) return "Fail: +0 not found" + if (A(-0f) !in s) return "Fail: -0 not found" + if (A(-2.72f) !in s) return "Fail: -2.72 not found" + if (A(NAN) !in s) return "Fail: NaN not found" + + return if (s.size == 5) "OK" else "Fail $s" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 1948b9ea388..f1e40644885 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -766,6 +766,18 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("doubleParam.kt") + public void testDoubleParam() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/dataClasses/doubleParam.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("floatParam.kt") + public void testFloatParam() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/dataClasses/floatParam.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("genericParam.kt") public void testGenericParam() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/dataClasses/genericParam.kt");