summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-09-09 13:04:31 +0200
committerJason Zaugg <jzaugg@gmail.com>2012-09-09 13:04:31 +0200
commit21bd081540413a8625247d2e40506112cc1ea218 (patch)
treefa646e5bbd367dc62f9dd87ae0a7c8bc36208602
parentaedb8db47338637430672b145cfc11e8d89441b9 (diff)
downloadscala-21bd081540413a8625247d2e40506112cc1ea218.tar.gz
scala-21bd081540413a8625247d2e40506112cc1ea218.tar.bz2
scala-21bd081540413a8625247d2e40506112cc1ea218.zip
Improve Constant#hashCode
- Incorporate `tag`, which is considered by equals, to reduce collisions. - Use the result of floatToRawIntBits(value) / doubleToRawLongBits(value), rather than value. This wasn't strictly necessary as (0d.## == (-0d).##) but this is more obviously correct.
-rw-r--r--src/reflect/scala/reflect/internal/Constants.scala40
1 files changed, 29 insertions, 11 deletions
diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala
index 4d512e3864..b434be64a3 100644
--- a/src/reflect/scala/reflect/internal/Constants.scala
+++ b/src/reflect/scala/reflect/internal/Constants.scala
@@ -31,6 +31,9 @@ trait Constants extends api.Constants {
final val EnumTag = 13
case class Constant(value: Any) extends ConstantApi {
+ import java.lang.Double.doubleToRawLongBits
+ import java.lang.Float.floatToRawIntBits
+
val tag: Int = value match {
case null => NullTag
case x: Unit => UnitTag
@@ -81,18 +84,10 @@ trait Constants extends api.Constants {
/** We need the equals method to take account of tags as well as values.
*/
+ // !!! In what circumstance could `equalHashValue == that.equalHashValue && tag != that.tag` be true?
override def equals(other: Any): Boolean = other match {
case that: Constant =>
- // Consider two NaNs to be identical, despite non-equality
- // Consider -0d to be distinct from 0d, despite equality
- import java.lang.Double.doubleToRawLongBits
- import java.lang.Float.floatToRawIntBits
-
- this.tag == that.tag && ((value, that.value) match {
- case (f1: Float, f2: Float) => floatToRawIntBits(f1) == floatToRawIntBits(f2)
- case (d1: Double, d2: Double) => doubleToRawLongBits(d1) == doubleToRawLongBits(d2)
- case (v1, v2) => v1 == v2
- })
+ this.tag == that.tag && equalHashValue == that.equalHashValue
case _ => false
}
@@ -244,7 +239,30 @@ trait Constants extends api.Constants {
def typeValue: Type = value.asInstanceOf[Type]
def symbolValue: Symbol = value.asInstanceOf[Symbol]
- override def hashCode: Int = value.## * 41 + 17
+ /**
+ * Consider two `NaN`s to be identical, despite non-equality
+ * Consider -0d to be distinct from 0d, despite equality
+ *
+ * We use the raw versions (i.e. `floatToRawIntBits` rather than `floatToIntBits`)
+ * to avoid treating different encodings of `NaN` as the same constant.
+ * You probably can't express different `NaN` varieties as compile time
+ * constants in regular Scala code, but it is conceivable that you could
+ * conjure them with a macro.
+ */
+ private def equalHashValue: Any = value match {
+ case f: Float => floatToRawIntBits(f)
+ case d: Double => doubleToRawLongBits(d)
+ case v => v
+ }
+
+ override def hashCode: Int = {
+ import scala.util.hashing.MurmurHash3._
+ val seed = 17
+ var h = seed
+ h = mix(h, tag.##) // include tag in the hash, otherwise 0, 0d, 0L, 0f collide.
+ h = mix(h, equalHashValue.##)
+ finalizeHash(h, length = 2)
+ }
}
object Constant extends ConstantExtractor