summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-07-01 15:28:03 +0000
committerPaul Phillips <paulp@improving.org>2011-07-01 15:28:03 +0000
commitab3e6f21aebeb24075483fbefab48a13e65d48a4 (patch)
treef87501c307951fd46b046c3ddf65cc240c65914a
parent3c5f893b78e5cfc7d0124cece34daea257e649ff (diff)
downloadscala-ab3e6f21aebeb24075483fbefab48a13e65d48a4.tar.gz
scala-ab3e6f21aebeb24075483fbefab48a13e65d48a4.tar.bz2
scala-ab3e6f21aebeb24075483fbefab48a13e65d48a4.zip
Stable hashCodes for case objects.
Somehow case objects fell through the cracks and have had default hashCode implementations, leading to new hashCodes on each jvm run. Now we use the productPrefix, and inline it right into the bytecode. scala> None.## == "None".## res0: Boolean = true Closes #4752, no review.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala8
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala30
-rw-r--r--test/files/run/bug4752.scala10
3 files changed, 37 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index c855abd7fb..0917646f76 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -126,6 +126,13 @@ trait SyntheticMethods extends ast.TreeDSL {
val method = syntheticMethod(nme.toString_, FINAL, makeNoArgConstructor(StringClass.tpe))
typer typed { DEF(method) === LIT(clazz.name.decode) }
}
+ def moduleHashCodeMethod: Tree = {
+ val method = syntheticMethod(nme.hashCode_, FINAL, makeNoArgConstructor(IntClass.tpe))
+ // The string being used as hashcode basis is also productPrefix.
+ val code = clazz.name.decode.hashCode
+
+ typer typed { DEF(method) === LIT(code) }
+ }
def forwardingMethod(name: Name, targetName: Name): Tree = {
val target = getMember(ScalaRunTimeModule, targetName)
@@ -262,6 +269,7 @@ trait SyntheticMethods extends ast.TreeDSL {
)
// methods for case objects only
def objectMethods = List(
+ Object_hashCode -> (() => moduleHashCodeMethod),
Object_toString -> (() => moduleToStringMethod)
)
// methods for both classes and objects
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index 5a20b72fcd..4d5f783d61 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -177,18 +177,26 @@ object ScalaRunTime {
def _hashCode(x: Product): Int = {
import scala.util.MurmurHash._
val arr = x.productArity
- var h = startHash(arr)
- var c = startMagicA
- var k = startMagicB
- var i = 0
- while (i < arr) {
- val elem = x.productElement(i)
- h = extendHash(h, elem.##, c, k)
- c = nextMagicA(c)
- k = nextMagicB(k)
- i += 1
+ // Case objects have the hashCode inlined directly into the
+ // synthetic hashCode method, but this method should still give
+ // a correct result if passed a case object.
+ if (arr == 0) {
+ x.productPrefix.hashCode
+ }
+ else {
+ var h = startHash(arr)
+ var c = startMagicA
+ var k = startMagicB
+ var i = 0
+ while (i < arr) {
+ val elem = x.productElement(i)
+ h = extendHash(h, elem.##, c, k)
+ c = nextMagicA(c)
+ k = nextMagicB(k)
+ i += 1
+ }
+ finalizeHash(h)
}
- finalizeHash(h)
}
/** Fast path equality method for inlining; used when -optimise is set.
diff --git a/test/files/run/bug4752.scala b/test/files/run/bug4752.scala
new file mode 100644
index 0000000000..3d5c166a7a
--- /dev/null
+++ b/test/files/run/bug4752.scala
@@ -0,0 +1,10 @@
+object Test {
+ object Bippy {
+ case object Dingus
+ }
+
+ def main(args: Array[String]): Unit = {
+ assert(None.## == "None".##, None)
+ assert(Test.Bippy.Dingus.## == "Dingus".##, Test.Bippy.Dingus)
+ }
+}