Aladdin - Scala Bugtracking
[#1111] project: compiler priority: low category: bug
submitter assigned to status date submitted
Nikolay Nikolay fixed 2007-05-15 20:37:50.0
subject [contrib #473] wrong Comparator (or compiler)
code
val l = Math.MAX_LONG

val b = BigInt(l)
val b2 = b * 2

println(b2.==(-2L))
println(b2.equals(-2L))
what happened
prints:

true
false
what expected should print: false false
[back to overview]
Changes of this bug report
Nikolay  edited on  2007-05-15 20:59:26.0
I assigned this contribution #474 to myself but it calls for discussion. Esentially, the problem is that the BigInt.equals method correctly returns false but we ignore it in the Comparator.equals method and squeeze the BigInt into a long because the counterpart in the comparison is boxed long. Maybe we should only perform the additional checks if both sides are Java boxes and simply trust 'equals' to do the right thing for anything else. Any thoughts?
Martin  edited on  2007-05-16 16:37:44.0
I fixed this by turning the comparator logic around. A primitive comparison is only executed if both operands are known to be in the domain of the comparison. That said, there should be an architecture that also ensures sane equality for different user-defined number types. Right now it works for bigint/bigint, bigint/int, int/bigint, say, but it does not necessarily work for bigint/decimal. Code is attached.
public class Comparator {

    private static int CHAR = 0, BYTE = 1, SHORT = 2, INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7; 
    
    private static int typeCode(Object a) {
        if (a instanceof Integer) return INT;
        if (a instanceof Character) return CHAR;
        if (a instanceof Long) return LONG;
        if (a instanceof Double) return DOUBLE;
        if (a instanceof Float) return FLOAT;
        if (a instanceof Byte) return BYTE;
        if (a instanceof Short) return SHORT;
        return OTHER;
    }

    /** A rich implementation of the equals method that overrides the default
     *  equals because Java's boxed primitives are utterly broken. This equals
     *  is inserted instead of a normal equals by the Scala compiler (in the
     *  ICode phase, method genEqEqPrimitive) only when either
     *  side of the comparison is a subclass of AnyVal, of
     *  java.lang.Number, of java.lang.Character or
     *  is exactly Any or AnyRef.
     */
    public static boolean equals(Object a, Object b) {
        if (a == null)
            return b == null;
        else if (a.equals(b))
            return true;
        else {
            int acode = typeCode(a);
            int bcode = typeCode(b);
            int maxcode = (acode < bcode) ? bcode : acode;
            if (maxcode <= INT) {
                int aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).intValue();
                int bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).intValue();
                return aa == bb;
            } else if (maxcode <= LONG) {
                long aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).longValue();
                long bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).longValue();
                return aa == bb;
            } else if (maxcode <= FLOAT) {
                float aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).floatValue();
                float bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).floatValue();
                return aa == bb;
            } else if (maxcode <= DOUBLE) {
                double aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).doubleValue();
                double bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).doubleValue();
                return aa == bb;
            } else {
                return a == b;
            }
        }
    }
}
Martin  edited on  2007-05-16 16:44:45.0
The previous version of equals still does not work. You have to include one more case:
...     
            } else if (acode != OTHER) {
                return b.equals(a);
            } else {
                return a == b;
            }
        }