summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala10
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala25
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala6
-rw-r--r--src/library/scala/Predef.scala7
-rw-r--r--src/library/scala/runtime/BoxesRunTime.java129
-rw-r--r--src/library/scala/runtime/Equality.java89
-rw-r--r--test/files/run/hashCodeBoxesRunTime.scala28
8 files changed, 98 insertions, 209 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 95c3a56767..0f59d9a3d9 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -839,16 +839,6 @@ trait ScalacSettings {
val YhigherKindedRaw = BooleanSetting ("-Yhigher-kinded-raw", "(temporary!) Treat raw Java types as higher-kinded types.")
val Yjenkins = BooleanSetting ("-Yjenkins-hashCodes", "Use jenkins hash algorithm for case class generated hashCodes.")
- // Equality specific
- val logEqEq = BooleanSetting ("-Ylog-eqeq", "Log all noteworthy equality tests") .
- withPostSetHook(() => scala.runtime.Equality.logEverything = true)
- val YfutureEqEq = BooleanSetting ("-Yfuture-eqeq", "Use proposed overloading-based numeric equality semantics.") .
- withPostSetHook(() => scala.runtime.Equality.use28Semantics = true)
- val YwarnEqEq = BooleanSetting ("-Ywarn-eqeq", "Warn when boxed primitives of different types are compared.") .
- withPostSetHook(() => scala.runtime.Equality.warnOnBoxedCompare = true)
- val YdieEqEq = BooleanSetting ("-Ydie-changed-eqeq", "Throw an exception if a comparison would have come back differently in scala 2.7.") .
- withPostSetHook(() => scala.runtime.Equality.dieOnBoxedCompareIfValuesAreEqual = true)
-
// Warnings
val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics")
val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference")
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index d12bf0c2c5..e1ddd260cb 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1292,9 +1292,6 @@ abstract class GenICode extends SubComponent {
elseCtx: Context): Unit =
{
def genComparisonOp(l: Tree, r: Tree, code: Int) {
- if (settings.logEqEq.value && isUniversalEqualityOp(code))
- logEqEq(tree, l, r, code)
-
val op: TestOp = code match {
case scalaPrimitives.LT => LT
case scalaPrimitives.LE => LE
@@ -1423,13 +1420,8 @@ abstract class GenICode extends SubComponent {
(rsym == ObjectClass) ||
(lsym != rsym) && (isBoxed(lsym) || isBoxed(rsym))
}
- def cannotAvoidBoxesRuntime =
- settings.logEqEq.value || settings.YwarnEqEq.value || settings.YdieEqEq.value
- /** We can avoid generating calls to BoxesRuntime only if -Yfuture-eqeq
- * is enabled AND none of the eqeq logging options are enabled.
- */
- if (mustUseAnyComparator && (!settings.YfutureEqEq.value || cannotAvoidBoxesRuntime)) {
+ if (mustUseAnyComparator) {
var equalsMethod = BoxesRunTime_equals
// when -optimise is on we call the @inline-version of equals, found in ScalaRunTime
if (settings.XO.value) {
@@ -2161,19 +2153,4 @@ abstract class GenICode extends SubComponent {
override def toString() = "[]"
override def varsInScope: Buffer[Local] = new ListBuffer
}
-
- /** Log equality tests between different primitives. */
- def logEqEq(tree: Tree, l: Tree, r: Tree, code: Int) {
- import definitions._
- val op = if (code == scalaPrimitives.EQ) "==" else if (code == scalaPrimitives.NE) "!=" else "??"
- val tkl = toTypeKind(l.tpe)
- val tkr = toTypeKind(r.tpe)
-
- if (tkl.isNumericType && tkr.isNumericType && tkl != tkr)
- runtime.Equality.logComparison(
- "Comparing actual primitives",
- "%s %s %s".format(l.tpe, op, r.tpe),
- tree.pos.source + ":" + tree.pos.line
- )
- }
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 365bff38e6..cb9c681075 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -557,11 +557,9 @@ trait Definitions {
mclass.setInfo(ClassInfoType(List(), new Scope, mclass))
module.setInfo(mclass.tpe)
- val box = newMethod(mclass, nme.box, List(clazz.typeConstructor),
- ObjectClass.typeConstructor)
+ val box = newMethod(mclass, nme.box, List(clazz.typeConstructor), boxedClass(clazz).tpe)
boxMethod(clazz) = box
- val unbox = newMethod(mclass, nme.unbox, List(ObjectClass.typeConstructor),
- clazz.typeConstructor)
+ val unbox = newMethod(mclass, nme.unbox, List(ObjectClass.typeConstructor), clazz.typeConstructor)
unboxMethod(clazz) = unbox
clazz
@@ -624,10 +622,11 @@ trait Definitions {
// def +(s: String): String
newMethod(clazz, nme.ADD, List(stringtype), stringtype)
- val restype = clazz match {
- case LongClass | FloatClass | DoubleClass => clazz.typeConstructor
- case _ => inttype
+ def isLongFloatOrDouble = clazz match {
+ case LongClass | FloatClass | DoubleClass => true
+ case _ => false
}
+ val restype = if (isLongFloatOrDouble) clazz.typeConstructor else inttype
// shift operations
if (isCardinal)
diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
index e8bb4581f1..ba328b9f48 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
@@ -152,12 +152,6 @@ abstract class ConstantFolder {
}
private def foldBinop(op: Name, x: Constant, y: Constant): Constant = {
- // temporarily logging folded ==/!= so the log doesn't have unexplained absences
- // Careful, these four lines added 3 minutes to the time to compile this file under -optimise
- // if ((op == nme.EQ || op == nme.NE) && x.tag != y.tag && settings.logEqEq.value) {
- // val opstr = if (op == nme.EQ) "==" else "!="
- // scala.runtime.Equality.log("Folding constant expression (%s %s %s)".format(x.value, opstr, y.value))
- // }
val optag =
if (x.tag == y.tag) x.tag
else if (isNumeric(x.tag) && isNumeric(y.tag)) Math.max(x.tag, y.tag)
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index c3e0548183..c44ec72299 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -96,12 +96,13 @@ object Predef extends LowPriorityImplicits {
// hashcode -----------------------------------------------------------
@inline def hash(x: Any): Int =
- if (x.isInstanceOf[Number]) runtime.BoxesRunTime.numHash(x.asInstanceOf[Number]) else x.hashCode
+ if (x.isInstanceOf[Number]) runtime.BoxesRunTime.hashFromNumber(x.asInstanceOf[Number])
+ else x.hashCode
@inline def hash(x: Number): Int =
- runtime.BoxesRunTime.numHash(x.asInstanceOf[Number])
+ runtime.BoxesRunTime.hashFromNumber(x)
- @inline def hash(x: Long): Int = {
+ @inline def hash(x: java.lang.Long): Int = {
val iv = x.intValue
if (iv == x.longValue) iv else x.hashCode
}
diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java
index c965bc2e95..b4252ed647 100644
--- a/src/library/scala/runtime/BoxesRunTime.java
+++ b/src/library/scala/runtime/BoxesRunTime.java
@@ -178,76 +178,65 @@ public class BoxesRunTime
return x.intValue() == ch;
}
- public static int numHash(Number n) {
- if (n instanceof Long) {
- int iv = n.intValue();
- long lv = n.longValue();
- if (lv == iv) return iv;
- } else if (n instanceof Float) {
- int iv = n.intValue();
- float fv = n.floatValue();
- if (fv == iv) return iv;
- long lv = n.longValue();
- if (fv == lv) return new Long(lv).hashCode();
- } else if (n instanceof Double) {
- int iv = n.intValue();
- double dv = n.doubleValue();
- if (dv == iv) return iv;
- float fv = n.floatValue();
- if (dv == fv) return new Float(fv).hashCode();
- long lv = n.longValue();
- if (dv == lv) return new Long(lv).hashCode();
- }
- return n.hashCode();
- }
-
- private static boolean equalsBonusLogicFromScala27(Object a, Object b) {
- if (a instanceof Number || a instanceof Character || b instanceof Number || b instanceof Character) {
- int acode = typeCode(a);
- int bcode = typeCode(b);
- int maxcode = (acode < bcode) ? bcode : acode;
- boolean res = false;
- if (maxcode <= INT) {
- int aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).intValue();
- int bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).intValue();
- res = (aa == bb);
- }
- if (maxcode <= LONG) {
- long aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).longValue();
- long bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).longValue();
- res = (aa == bb);
- }
- if (maxcode <= FLOAT) {
- float aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).floatValue();
- float bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).floatValue();
- res = (aa == bb);
- }
- if (maxcode <= DOUBLE) {
- double aa = (acode == CHAR) ? ((Character) a).charValue() : ((Number) a).doubleValue();
- double bb = (bcode == CHAR) ? ((Character) b).charValue() : ((Number) b).doubleValue();
- res = (aa == bb);
- }
-
- if (res || b.equals(a)) return true;
- else return false;
- }
- return false;
- }
-
- private static String logMessage(Object a, Object b, String where) {
- return "Compared boxed primitives (" + boxDescription(a) + " == " + boxDescription(b) + ") @ " + where;
- }
-
- private static void verifyEqEq(Object a, Object b, boolean isFatal, boolean onlyIfActuallyEqual) {
- int code1 = typeCode(a);
- int code2 = typeCode(b);
-
- if (code1 < OTHER && code2 < OTHER && code1 != code2) {
- if (!onlyIfActuallyEqual || equalsBonusLogicFromScala27(a, b)) {
- String msg = logMessage(a, b, Equality.whereAreWe(5));
- Equality.warnOrDie(msg, isFatal);
- }
- }
+ /** Hashcode algorithm is driven by the requirements imposed
+ * by primitive equality semantics, namely that equal objects
+ * have equal hashCodes. The first priority are the integral/char
+ * types, which already have the same hashCodes for the same
+ * values except for Long. So Long's hashCode is altered to
+ * conform to Int's for all values in Int's range.
+ *
+ * Float is problematic because it's far too small to hold
+ * all the Ints, so for instance Int.MaxValue.toFloat claims
+ * to be == to each of the largest 64 Ints. There is no way
+ * to preserve equals/hashCode alignment without compromising
+ * the hashCode distribution, so Floats are only guaranteed
+ * to have the same hashCode for whole Floats in the range
+ * Short.MinValue to Short.MaxValue (2^16 total.)
+ *
+ * Double has its hashCode altered to match the entire Int range,
+ * but is not guaranteed beyond that. (But could/should it be?
+ * The hashCode is only 32 bits so this is a more tractable
+ * issue than Float's, but it might be better simply to exclude it.)
+ *
+ * Note: BigInt and BigDecimal, being arbitrary precision, could
+ * be made consistent with all other types for the Int range, but
+ * as yet have not.
+ *
+ * Note: Among primitives, Float.NaN != Float.NaN, but the boxed
+ * verisons are equal. This still needs reconciliation.
+ */
+ public static int hashFromLong(Long n) {
+ int iv = n.intValue();
+ if (iv == n.longValue()) return iv;
+ else return n.hashCode();
+ }
+ public static int hashFromDouble(Double n) {
+ int iv = n.intValue();
+ double dv = n.doubleValue();
+ if (iv == dv) return iv;
+
+ long lv = n.longValue();
+ if (lv == dv) return Long.valueOf(lv).hashCode();
+ else return n.hashCode();
+ }
+ public static int hashFromFloat(Float n) {
+ int iv = n.intValue();
+ float fv = n.floatValue();
+ if (iv == fv) return iv;
+
+ long lv = n.longValue();
+ if (lv == fv) return Long.valueOf(lv).hashCode();
+ else return n.hashCode();
+ }
+ public static int hashFromNumber(Number n) {
+ if (n instanceof Long) return hashFromLong((Long)n);
+ else if (n instanceof Double) return hashFromDouble((Double)n);
+ else if (n instanceof Float) return hashFromFloat((Float)n);
+ else return n.hashCode();
+ }
+ public static int hashFromObject(Object a) {
+ if (a instanceof Number) return hashFromNumber((Number)a);
+ else return a.hashCode();
}
/* OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS ... OPERATORS */
diff --git a/src/library/scala/runtime/Equality.java b/src/library/scala/runtime/Equality.java
deleted file mode 100644
index 52ed7005cd..0000000000
--- a/src/library/scala/runtime/Equality.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ Scala API **
-** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
-** /____/\___/_/ |_/____/_/ | | **
-** |/ **
-\* */
-
-// $Id$
-
-
-package scala.runtime;
-
-import java.io.*;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.logging.Handler;
-import java.util.logging.StreamHandler;
-import java.util.logging.SimpleFormatter;
-
-/** An object (static class) encapsulating the variations on equality
- * presently under consideration. It's written in java so it can easily
- * be made available to BoxesRunTime as well as scala code.
- */
-public class Equality
-{
- public static boolean logEverything = false;
- public static boolean use28Semantics = false;
-
- public static boolean warnOnBoxedCompare = false;
- public static boolean warnOnBoxedCompareIfValuesAreEqual = false;
- public static boolean dieOnBoxedCompare = false;
- public static boolean dieOnBoxedCompareIfValuesAreEqual = false;
-
- private static Handler handler;
- public static Logger logger;
- public static final Logger defaultLogger;
-
- static {
- class EqualityLogger extends Logger {
- EqualityLogger() {
- super("EqualityLogger", null);
- }
- }
- defaultLogger = new EqualityLogger();
-
- handler = new StreamHandler(System.out, new SimpleFormatter());
- handler.setLevel(Level.INFO);
- defaultLogger.addHandler(handler);
-
- logger = defaultLogger;
- }
-
- public static void warnOrDie(String msg, boolean isFatal) {
- if (isFatal) throw new RuntimeException(msg);
- else log(msg);
- // else System.out.println(msg);
- }
-
- public static String obToString(Object o) {
- String s = o.toString() + " (" + o.getClass().getSimpleName() + ")";
- return s.replaceAll("\\n", " ");
- }
-
- public static void logComparison(String msg, String cmp, String where) {
- log(String.format("%s (%s) at %s", msg, cmp, where));
- }
-
- public static String whereAreWe() { return whereAreWe(4); }
- public static String whereAreWe(int depth) {
- StackTraceElement[] es = Thread.currentThread().getStackTrace();
- if (es.length < depth)
- return "<unknown>";
-
- StackTraceElement e = es[depth - 1];
- String clazz = e.getClassName().replaceAll("\\$.*$", "\\$...");
- return String.format("%s.%s(%s.%d)", clazz, e.getMethodName(), e.getFileName(), e.getLineNumber());
- }
-
- public static void log(String msg) {
- if (logEverything) {
- if (logger != null) {
- logger.warning(msg);
- handler.flush();
- }
- else System.out.println(msg);
- }
- }
-}
diff --git a/test/files/run/hashCodeBoxesRunTime.scala b/test/files/run/hashCodeBoxesRunTime.scala
new file mode 100644
index 0000000000..3eacacb663
--- /dev/null
+++ b/test/files/run/hashCodeBoxesRunTime.scala
@@ -0,0 +1,28 @@
+// This only tests direct access to the methods in BoxesRunTime,
+// not the whole scheme.
+object Test
+{
+ import java.{ lang => jl }
+ import scala.runtime.BoxesRunTime.{ hashFromNumber, hashFromObject }
+
+ def allSame[T](xs: List[T]) = assert(xs.removeDuplicates.size == 1, "failed: " + xs)
+
+ def mkNumbers(x: Int): List[Number] =
+ List(x.toByte, x.toShort, x, x.toLong, x.toFloat, x.toDouble)
+
+ def testLDF(x: Long) = allSame(List[Number](x, x.toDouble, x.toFloat) map hashFromNumber)
+
+ def main(args: Array[String]): Unit = {
+ List(Byte.MinValue, -1, 0, 1, Byte.MaxValue) foreach { n =>
+ val hashes = mkNumbers(n) map hashFromNumber
+ allSame(hashes)
+ if (n >= 0) {
+ val charCode = hashFromObject(n.toChar: Character)
+ assert(charCode == hashes.head)
+ }
+ }
+
+ testLDF(Short.MaxValue.toLong)
+ testLDF(Short.MinValue.toLong)
+ }
+}