summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-03-19 21:48:42 +0000
committerPaul Phillips <paulp@improving.org>2010-03-19 21:48:42 +0000
commit6613b1cdae66ae23edfd0e20eb5d4d066018681d (patch)
treec033a214a3d7e15ca01cdf778b88ac6e026c0fb7
parentf2be3e6836014b3e4db9c7eca3bb4da8ab447f89 (diff)
downloadscala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.tar.gz
scala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.tar.bz2
scala-6613b1cdae66ae23edfd0e20eb5d4d066018681d.zip
Returning to the thrilling world of equality an...
Returning to the thrilling world of equality and hashCodes now that Any.## is a reality. Moved the hash functions from Predef to ScalaRunTime, and made what appears to be an optimization to equals by not losing the result of an instanceof test. Review by community.
-rw-r--r--src/compiler/scala/tools/nsc/backend/JavaPlatform.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala30
-rw-r--r--src/library/scala/Predef.scala16
-rw-r--r--src/library/scala/runtime/BoxesRunTime.java103
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala43
-rw-r--r--test/files/run/equality.scala2
6 files changed, 125 insertions, 72 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
index 129a747e64..c4365a82ac 100644
--- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
@@ -26,6 +26,9 @@ trait JavaPlatform extends Platform[AbstractFile] {
) ::: depAnalysisPhase
lazy val externalEquals = getMember(BoxesRunTimeClass, nme.equals_)
+ def externalEqualsNumNum = getMember(BoxesRunTimeClass, "equalsNumNum")
+ def externalEqualsNumChar = getMember(BoxesRunTimeClass, "equalsNumChar")
+ def externalEqualsNumObject = getMember(BoxesRunTimeClass, "equalsNumObject")
def isMaybeBoxed(sym: Symbol): Boolean = {
import definitions._
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 64e6759da2..77e14139ba 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -9,7 +9,8 @@ package scala.tools.nsc
package backend
package icode
-import scala.collection.mutable.{Map, HashMap, ListBuffer, Buffer, HashSet}
+import scala.collection.{ mutable, immutable }
+import scala.collection.mutable.{ HashMap, ListBuffer, Buffer, HashSet }
import scala.tools.nsc.symtab._
import scala.annotation.switch
import PartialFunction._
@@ -28,6 +29,7 @@ abstract class GenICode extends SubComponent {
import definitions.{
ArrayClass, ObjectClass, ThrowableClass, StringClass, NothingClass, NullClass,
Object_equals, Object_isInstanceOf, Object_asInstanceOf, ScalaRunTimeModule,
+ BoxedNumberClass, BoxedCharacterClass,
getMember
}
import scalaPrimitives.{
@@ -1362,13 +1364,29 @@ abstract class GenICode extends SubComponent {
* comparison might have a run-time type subtype of java.lang.Number or java.lang.Character.
* When it is statically known that both sides are equal and subtypes of Number of Character,
* not using the rich equality is possible (their own equals method will do ok.)*/
- def mustUseAnyComparator: Boolean =
- isMaybeBoxed(l.tpe.typeSymbol) && isMaybeBoxed(r.tpe.typeSymbol)
+ def mustUseAnyComparator: Boolean = {
+ def areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe)
+ !areSameFinals && isMaybeBoxed(l.tpe.typeSymbol) && isMaybeBoxed(r.tpe.typeSymbol)
+ }
if (mustUseAnyComparator) {
// when -optimise is on we call the @inline-version of equals, found in ScalaRunTime
val equalsMethod =
- if (!settings.XO.value) platform.externalEquals
+ if (!settings.XO.value) {
+ def default = platform.externalEquals
+ platform match {
+ case x: JavaPlatform =>
+ import x._
+ if (l.tpe <:< BoxedNumberClass.tpe) {
+ if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum
+ else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar
+ else externalEqualsNumObject
+ }
+ else default
+
+ case _ => default
+ }
+ }
else {
ctx.bb.emit(LOAD_MODULE(ScalaRunTimeModule))
getMember(ScalaRunTimeModule, nme.inlinedEquals)
@@ -1603,7 +1621,7 @@ abstract class GenICode extends SubComponent {
* to delay it any more: they will be used at some point.
*/
class DuplicateLabels(boundLabels: collection.Set[Symbol]) extends Transformer {
- val labels: Map[Symbol, Symbol] = new HashMap
+ val labels: mutable.Map[Symbol, Symbol] = new HashMap
var method: Symbol = _
var ctx: Context = _
@@ -2073,7 +2091,7 @@ abstract class GenICode extends SubComponent {
* jumps to the given basic block.
*/
def patch(code: Code) {
- def substMap: Map[Instruction, Instruction] = {
+ def substMap: mutable.Map[Instruction, Instruction] = {
val map = new HashMap[Instruction, Instruction]()
toPatch foreach (i => map += (i -> patch(i)))
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index 760d215d3f..d022b3e4a5 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -53,22 +53,6 @@ object Predef extends LowPriorityImplicits {
@inline def locally[T](x: T): T = x
- // hashcode -----------------------------------------------------------
-
- @inline def hash(x: Any): Int =
- if (x.isInstanceOf[Number]) runtime.BoxesRunTime.hashFromNumber(x.asInstanceOf[Number])
- else x.hashCode
-
- @inline def hash(x: Number): Int =
- runtime.BoxesRunTime.hashFromNumber(x)
-
- @inline def hash(x: java.lang.Long): Int = {
- val iv = x.intValue
- if (iv == x.longValue) iv else x.hashCode
- }
-
- @inline def hash(x: Int): Int = x
-
// errors and asserts -------------------------------------------------
def error(message: String): Nothing = throw new RuntimeException(message)
diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java
index 087331e1c5..7c27835b5a 100644
--- a/src/library/scala/runtime/BoxesRunTime.java
+++ b/src/library/scala/runtime/BoxesRunTime.java
@@ -28,7 +28,7 @@ import scala.math.ScalaNumber;
* @author Martin Odersky
* @contributor Stepan Koltsov
* @version 2.0 */
-public class BoxesRunTime
+public final class BoxesRunTime
{
private static final int CHAR = 0, BYTE = 1, SHORT = 2, INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7;
@@ -136,38 +136,51 @@ public class BoxesRunTime
* in any case, we dispatch to it as soon as we spot one on either side.
*/
public static boolean equals2(Object x, Object y) {
- if (x instanceof Number) {
- Number xn = (Number)x;
-
- if (y instanceof Number) {
- Number yn = (Number)y;
- int xcode = eqTypeCode(xn);
- int ycode = eqTypeCode(yn);
- switch (ycode > xcode ? ycode : xcode) {
- case INT:
- return xn.intValue() == yn.intValue();
- case LONG:
- return xn.longValue() == yn.longValue();
- case FLOAT:
- return xn.floatValue() == yn.floatValue();
- case DOUBLE:
- return xn.doubleValue() == yn.doubleValue();
- default:
- if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber))
- return y.equals(x);
- }
- } else if (y instanceof Character)
- return equalsNumChar(xn, (Character)y);
- } else if (x instanceof Character) {
- Character xc = (Character)x;
- if (y instanceof Character)
- return xc.charValue() == ((Character)y).charValue();
- if (y instanceof Number)
- return equalsNumChar((Number)y, xc);
- }
+ if (x instanceof Number)
+ return equalsNumObject((Number)x, y);
+ if (x instanceof Character)
+ return equalsCharObject((Character)x, y);
+
return x.equals(y);
}
+ public static boolean equalsNumObject(Number xn, Object y) {
+ if (y instanceof Number)
+ return equalsNumNum(xn, (Number)y);
+ else if (y instanceof Character)
+ return equalsNumChar(xn, (Character)y);
+
+ return xn.equals(y);
+ }
+
+ public static boolean equalsNumNum(Number xn, Number yn) {
+ int xcode = eqTypeCode(xn);
+ int ycode = eqTypeCode(yn);
+ switch (ycode > xcode ? ycode : xcode) {
+ case INT:
+ return xn.intValue() == yn.intValue();
+ case LONG:
+ return xn.longValue() == yn.longValue();
+ case FLOAT:
+ return xn.floatValue() == yn.floatValue();
+ case DOUBLE:
+ return xn.doubleValue() == yn.doubleValue();
+ default:
+ if ((yn instanceof ScalaNumber) && !(xn instanceof ScalaNumber))
+ return yn.equals(xn);
+ }
+ return xn.equals(yn);
+ }
+
+ public static boolean equalsCharObject(Character xc, Object y) {
+ if (y instanceof Character)
+ return xc.charValue() == ((Character)y).charValue();
+ if (y instanceof Number)
+ return equalsNumChar((Number)y, xc);
+
+ return xc.equals(y);
+ }
+
private static boolean equalsNumChar(Number xn, Character yc) {
char ch = yc.charValue();
switch (eqTypeCode(xn)) {
@@ -212,27 +225,27 @@ public class BoxesRunTime
* 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();
+ 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;
+ 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();
+ 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;
+ 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();
+ 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);
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index d04824bae1..43d60f2383 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -166,7 +166,8 @@ object ScalaRunTime {
@inline def inlinedEquals(x: Object, y: Object): Boolean =
if (x eq y) true
else if (x eq null) false
- else if (x.isInstanceOf[java.lang.Number] || x.isInstanceOf[java.lang.Character]) BoxesRunTime.equals2(x, y)
+ else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.equalsNumObject(x.asInstanceOf[java.lang.Number], y)
+ else if (x.isInstanceOf[java.lang.Character]) BoxesRunTime.equalsCharObject(x.asInstanceOf[java.lang.Character], y)
else x.equals(y)
def _equals(x: Product, y: Any): Boolean = y match {
@@ -174,10 +175,42 @@ object ScalaRunTime {
case _ => false
}
- /** Just a stub for now, but I think this is where the Predef.hash
- * methods should be.
- */
- @inline def hash(x: Any): Int = Predef.hash(x)
+ // hashcode -----------------------------------------------------------
+
+ @inline def hash(x: Any): Int =
+ if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number])
+ else x.hashCode
+
+ @inline def hash(dv: Double): Int = {
+ val iv = dv.toInt
+ if (iv == dv) return iv
+
+ val lv = dv.toLong
+ if (lv == dv) return lv.hashCode
+ else dv.hashCode
+ }
+ @inline def hash(fv: Float): Int = {
+ val iv = fv.toInt
+ if (iv == fv) return iv
+
+ val lv = fv.toLong
+ if (lv == fv) return lv.hashCode
+ else fv.hashCode
+ }
+ @inline def hash(lv: Long): Int = {
+ val iv = lv.toInt
+ if (iv == lv) iv else lv.hashCode
+ }
+ @inline def hash(x: Int): Int = x
+ @inline def hash(x: Short): Int = x.toInt
+ @inline def hash(x: Byte): Int = x.toInt
+ @inline def hash(x: Char): Int = x.toInt
+
+ @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x)
+ @inline def hash(x: java.lang.Long): Int = {
+ val iv = x.intValue
+ if (iv == x.longValue) iv else x.hashCode
+ }
/** A helper method for constructing case class equality methods,
* because existential types get in the way of a clean outcome and
diff --git a/test/files/run/equality.scala b/test/files/run/equality.scala
index 5b9ad207da..6498b232e1 100644
--- a/test/files/run/equality.scala
+++ b/test/files/run/equality.scala
@@ -1,6 +1,8 @@
// a quickly assembled test of equality. Needs work.
object Test
{
+ import scala.runtime.ScalaRunTime.hash
+
def makeFromInt(x: Int) = List(
x.toByte, x.toShort, x.toInt, x.toLong, x.toFloat, x.toDouble, BigInt(x), BigDecimal(x)
) ::: (