From ba5dbbd44db2c23c7532cde13453c6a031afb6e5 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 28 Feb 2010 14:35:50 +0000 Subject: Added ## method to Any as our scala hashCode me... Added ## method to Any as our scala hashCode method which provides consistent answers for primitive types. And I'm sure we're all tired of new starrs, but it's hard to add a method to Any without one. This patch only brings ## into existence, but nothing calls it yet. // some true assertions scala> assert(5.5f.## == 5.5f.hashCode) scala> assert(5.0f.## != 5.0f.hashCode && 5.0f.## == 5L.##) No review. (Already reviewed by odersky.) --- lib/scala-compiler.jar.desired.sha1 | 2 +- lib/scala-library-src.jar.desired.sha1 | 2 +- lib/scala-library.jar.desired.sha1 | 2 +- .../scala/tools/nsc/backend/ScalaPrimitives.scala | 2 ++ .../scala/tools/nsc/backend/icode/GenICode.scala | 15 +++++++++++++++ src/compiler/scala/tools/nsc/symtab/Definitions.scala | 7 ++++++- src/compiler/scala/tools/nsc/symtab/StdNames.scala | 2 ++ src/compiler/scala/tools/nsc/transform/Erasure.scala | 4 ++++ .../scala/tools/nsc/typechecker/SuperAccessors.scala | 17 ++++++++++++----- src/library/scala/runtime/ScalaRunTime.scala | 5 +++++ test/files/run/hashhash.scala | 15 +++++++++++++++ 11 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 test/files/run/hashhash.scala diff --git a/lib/scala-compiler.jar.desired.sha1 b/lib/scala-compiler.jar.desired.sha1 index ea24ec0ce4..dc48e2f9ae 100644 --- a/lib/scala-compiler.jar.desired.sha1 +++ b/lib/scala-compiler.jar.desired.sha1 @@ -1 +1 @@ -9e9a205af73a635535b5308ba726084fe201a093 ?scala-compiler.jar +58217106efbefca262416284da22d8f935c0d5a6 ?scala-compiler.jar diff --git a/lib/scala-library-src.jar.desired.sha1 b/lib/scala-library-src.jar.desired.sha1 index 52f27a69b9..57f522e23d 100644 --- a/lib/scala-library-src.jar.desired.sha1 +++ b/lib/scala-library-src.jar.desired.sha1 @@ -1 +1 @@ -2ede2a2a8ef5a33d8c081c6e52df7674c6482c02 ?scala-library-src.jar +084aab3593eb7fbc5ffe68c3af565d258146a85b ?scala-library-src.jar diff --git a/lib/scala-library.jar.desired.sha1 b/lib/scala-library.jar.desired.sha1 index 0c15cfe795..842de98724 100644 --- a/lib/scala-library.jar.desired.sha1 +++ b/lib/scala-library.jar.desired.sha1 @@ -1 +1 @@ -7c64b2e74aae897887cf3741c0a8ce87b631f027 ?scala-library.jar +413b018f76f6684e9eb9dc04ea21097e3c59aaf7 ?scala-library.jar diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index d9830f7462..90261288ee 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -94,6 +94,7 @@ abstract class ScalaPrimitives { final val AS = 81 // x.as[y] final val ISERASED = 85 // x.is$erased[y] final val ASERASED = 86 // x.as$erased[y] + final val HASH = 87 // x.## // AnyRef operations final val SYNCHRONIZED = 90 // x.synchronized(y) @@ -215,6 +216,7 @@ abstract class ScalaPrimitives { addPrimitive(Any_!=, NE) addPrimitive(Any_isInstanceOf, IS) addPrimitive(Any_asInstanceOf, AS) + addPrimitive(Any_##, HASH) // java.lang.Object addPrimitive(Object_eq, ID) diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 048d0b7bec..35a5f80d1f 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -423,6 +423,8 @@ abstract class GenICode extends SubComponent { genArithmeticOp(tree, ctx, code) else if (code == scalaPrimitives.CONCAT) (genStringConcat(tree, ctx), STRING) + else if (code == scalaPrimitives.HASH) + (genScalaHash(receiver, ctx), INT) else if (isArrayOp(code)) genArrayOp(tree, ctx, code, expectedType) else if (isLogicalOp(code) || isComparisonOp(code)) { @@ -1167,6 +1169,19 @@ abstract class GenICode extends SubComponent { ctx1 } + /** Generate the scala ## method. + */ + def genScalaHash(tree: Tree, ctx: Context): Context = { + val hashMethod = { + ctx.bb.emit(LOAD_MODULE(definitions.ScalaRunTimeModule)) + definitions.getMember(definitions.ScalaRunTimeModule, "hash") + } + + val ctx1 = genLoad(tree, ctx, ANY_REF_CLASS) + ctx1.bb.emit(CALL_METHOD(hashMethod, Static(false))) + ctx1 + } + /** * Returns a list of trees that each should be concatenated, from * left to right. It turns a chained call like "a".+("b").+("c") into diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 11ec664904..0ea34235b2 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -151,6 +151,7 @@ trait Definitions extends reflect.generic.StandardDefinitions { def arrayUpdateMethod = getMember(ScalaRunTimeModule, "array_update") def arrayLengthMethod = getMember(ScalaRunTimeModule, "array_length") def arrayCloneMethod = getMember(ScalaRunTimeModule, "array_clone") + def scalaRuntimeHash = getMember(ScalaRunTimeModule, "hash") // classes with special meanings lazy val NotNullClass = getClass("scala.NotNull") @@ -388,12 +389,14 @@ trait Definitions extends reflect.generic.StandardDefinitions { var Any_toString : Symbol = _ var Any_isInstanceOf: Symbol = _ var Any_asInstanceOf: Symbol = _ + var Any_## : Symbol = _ // members of class java.lang.{Object, String} var Object_eq : Symbol = _ var Object_ne : Symbol = _ var Object_== : Symbol = _ var Object_!= : Symbol = _ + var Object_## : Symbol = _ var Object_synchronized: Symbol = _ lazy val Object_isInstanceOf = newPolyMethod( ObjectClass, "$isInstanceOf", @@ -769,13 +772,15 @@ trait Definitions extends reflect.generic.StandardDefinitions { Any_equals = newMethod(AnyClass, nme.equals_, anyparam, booltype) Any_hashCode = newMethod(AnyClass, nme.hashCode_, Nil, inttype) Any_toString = newMethod(AnyClass, nme.toString_, Nil, stringtype) + Any_## = newMethod(AnyClass, nme.HASHHASH, Nil, inttype) setFlag FINAL Any_isInstanceOf = newPolyMethod( AnyClass, nme.isInstanceOf_, tparam => booltype) setFlag FINAL Any_asInstanceOf = newPolyMethod( AnyClass, nme.asInstanceOf_, tparam => tparam.typeConstructor) setFlag FINAL - // members of class java.lang.{Object, String} + // members of class java.lang.{ Object, String } + Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype) setFlag FINAL Object_== = newMethod(ObjectClass, nme.EQ, anyrefparam, booltype) setFlag FINAL Object_!= = newMethod(ObjectClass, nme.NE, anyrefparam, booltype) setFlag FINAL Object_eq = newMethod(ObjectClass, nme.eq, anyrefparam, booltype) setFlag FINAL diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index 40e2551500..295dba2b46 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -214,6 +214,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable => val PERCENT = encode("%") val EQL = encode("=") val USCOREEQL = encode("_=") + val HASHHASH = encode("##") val Nothing = newTermName("Nothing") val Null = newTermName("Null") @@ -289,6 +290,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable => val getCause = newTermName("getCause") val getClass_ = newTermName("getClass") val getMethod_ = newTermName("getMethod") + val hash_ = newTermName("hash") val hashCode_ = newTermName("hashCode") val hasNext = newTermName("hasNext") val head = newTermName("head") diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 617967383a..79bc6bbba5 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -943,6 +943,10 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast. tree, SelectFromArray(qual, name, erasure(qual.tpe)).copyAttrs(fn), args) + + case Apply(fn @ Select(qual, _), Nil) if (fn.symbol == Any_## || fn.symbol == Object_##) => + Apply(gen.mkAttributedRef(scalaRuntimeHash), List(qual)) + case Apply(fn, args) => if (fn.symbol == Any_asInstanceOf) fn match { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 7012fa177a..252b1571a4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -26,6 +26,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // inherits abstract value `global' and class `Phase' from Transform import global._ + import definitions.{ IntClass, UnitClass, ByNameParamClass, Any_asInstanceOf, Object_## } /** the following two members override abstract members in Transform */ val phaseName: String = "superaccessors" @@ -44,7 +45,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT private def transformArgs(args: List[Tree], params: List[Symbol]) = ((args, params).zipped map { (arg, param) => - if (param.tpe.typeSymbol == definitions.ByNameParamClass) + if (param.tpe.typeSymbol == ByNameParamClass) withInvalidOwner { checkPackedConforms(transform(arg), param.tpe.typeArgs.head) } else transform(arg) }) ::: @@ -75,10 +76,16 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } - private def transformSuperSelect(tree: Tree) = tree match { - case Select(sup @ Super(_, mix), name) => + private def transformSuperSelect(tree: Tree): Tree = tree match { + // Intercept super.## and translate it to this.## + // which is fine since it's final. + case Select(sup @ Super(_, _), nme.HASHHASH) => + Select(gen.mkAttributedThis(sup.symbol), Object_##) setType IntClass.tpe + + case Select(sup @ Super(_, mix), name) => val sym = tree.symbol val clazz = sup.symbol + if (sym.isDeferred) { val member = sym.overridingSymbol(clazz); if (mix != nme.EMPTY.toTypeName || member == NoSymbol || @@ -338,7 +345,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } if (isDependentType) { val preciseTpe = expectedTpe.asSeenFrom(singleType(NoPrefix, obj), ownerClass) //typeRef(singleType(NoPrefix, obj), v.tpe.symbol, List()) - TypeApply(Select(res, definitions.Any_asInstanceOf), + TypeApply(Select(res, Any_asInstanceOf), List(TypeTree(preciseTpe))) } else res } @@ -364,7 +371,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (protAcc == NoSymbol) { protAcc = clazz.newMethod(field.pos, nme.protSetterName(field.originalName)) protAcc.setInfo(MethodType(protAcc.newSyntheticValueParams(List(clazz.typeOfThis, field.tpe)), - definitions.UnitClass.tpe)) + UnitClass.tpe)) clazz.info.decls.enter(protAcc) val code = DefDef(protAcc, { val obj :: value :: Nil = protAcc.paramss.head; diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index ca8549bffa..a65ec05401 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -174,6 +174,11 @@ 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) + /** Given any Scala value, convert it to a String. * * The primary motivation for this method is to provide a means for diff --git a/test/files/run/hashhash.scala b/test/files/run/hashhash.scala new file mode 100644 index 0000000000..4a34ab12e0 --- /dev/null +++ b/test/files/run/hashhash.scala @@ -0,0 +1,15 @@ +object Test +{ + class A { val x1 = this.## ; val x2 = super.## } + val myA = new A + assert(myA.x1 == myA.x2) + + def confirmSame(x: Any) = assert(x.## == x.hashCode, "%s.## != %s.hashCode".format(x, x)) + def confirmDifferent(x: Any) = assert(x.## != x.hashCode, "%s.## == %s.hashCode (but should not)".format(x, x)) + + def main(args: Array[String]): Unit = { + /** Just a little sanity check, not to be confused with a unit test. */ + List(5, 5.5f, "abc", new AnyRef, new A, ()) foreach confirmSame + List(5.0f, 1.0d, -(5.0f), (-1.0d)) foreach confirmDifferent + } +} -- cgit v1.2.3