From c43f01c39d029be1a82c78bb2f49da5eb6833ab5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 15 Nov 2009 11:14:57 +0000 Subject: Fixed #2848 and #2630; Improvements in equality... Fixed #2848 and #2630; Improvements in equality speed --- src/compiler/scala/tools/nsc/ast/Trees.scala | 47 +++++++++---- .../scala/tools/nsc/interactive/Global.scala | 18 ----- src/compiler/scala/tools/nsc/symtab/Types.scala | 14 ++-- .../scala/tools/nsc/typechecker/Contexts.scala | 10 +-- .../scala/tools/nsc/typechecker/Namers.scala | 12 ++-- src/library/scala/runtime/BoxesRunTime.java | 82 +++++++++++++--------- src/library/scala/runtime/ScalaRunTime.scala | 5 +- test/files/pos/t2484.scala | 17 +++++ 8 files changed, 115 insertions(+), 90 deletions(-) create mode 100755 test/files/pos/t2484.scala diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index bff4bd51c3..e4dfb14833 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -131,6 +131,8 @@ trait Trees { this } + /** Set tpe to give `tp` and return this. + */ def setType(tp: Type): this.type = { /*assert(kindingIrrelevant(tp) || !kindStar || !tp.isHigherKinded, tp+" should not be higher-kinded");*/ @@ -138,6 +140,15 @@ trait Trees { this } + /** Like `setType`, but if this is a previously empty TypeTree + * that fact is remembered so that resetType will snap back. + */ + def defineType(tp: Type): this.type = setType(tp) + + /** Reset type to `null`, with special handling of TypeTrees and the EmptyType + */ + def resetType() { tpe = null } + def symbol: Symbol = null def symbol_=(sym: Symbol) { throw new Error("symbol_= inapplicable for " + this) @@ -323,6 +334,7 @@ trait Trees { super.tpe_=(NoType) override def tpe_=(t: Type) = if (t != NoType) throw new Error("tpe_=("+t+") inapplicable for ") + override def resetType() {} override def isEmpty = true } @@ -868,12 +880,25 @@ trait Trees { case class TypeTree() extends TypTree { override def symbol = if (tpe == null) null else tpe.typeSymbol - private var orig: Tree = null // should be EmptyTree? + private var orig: Tree = null + private var wasEmpty: Boolean = false def original: Tree = orig def setOriginal(tree: Tree): this.type = { orig = tree; setPos(tree.pos); this } + override def defineType(tp: Type): this.type = { + wasEmpty = isEmpty + setType(tp) + } + + /** Reset type to null, unless type original was empty and then + * got its type via a defineType + */ + override def resetType() { + if (wasEmpty) tpe = null + } + override def isEmpty = (tpe eq null) || tpe == NoType } @@ -1817,18 +1842,16 @@ trait Trees { protected def isLocal(sym: Symbol): Boolean = true protected def resetDef(tree: Tree) { tree.symbol = NoSymbol - tree.tpe = null - super.traverse(tree) } - override def traverse(tree: Tree): Unit = tree match { - case EmptyTree | TypeTree() => - ; - case _: DefTree | Function(_, _) | Template(_, _, _) => - resetDef(tree) - case _ => - if (tree.hasSymbol && isLocal(tree.symbol)) tree.symbol = NoSymbol - tree.tpe = null - super.traverse(tree) + override def traverse(tree: Tree): Unit = { + tree match { + case _: DefTree | Function(_, _) | Template(_, _, _) => + resetDef(tree) + case _ => + if (tree.hasSymbol && isLocal(tree.symbol)) tree.symbol = NoSymbol + } + tree.resetType() + super.traverse(tree) } } diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index be3af86d53..4d2aa9f5da 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -443,24 +443,6 @@ self => } } - /** A traverser that resets all type and symbol attributes in a tree - object ResetAttrs extends Transformer { - override def transform(t: Tree): Tree = { - if (t.hasSymbol) t.symbol = NoSymbol - t match { - case EmptyTree => - t - case tt: TypeTree => - if (tt.original != null) tt.original - else t - case _ => - t.tpe = null - super.transform(t) - } - } - } - */ - /** The typer run */ class TyperRun extends Run { // units is always empty diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 35afe7cf5f..942890e2f1 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -4700,15 +4700,9 @@ A type's typeSymbol should never be inspected directly. else NothingClass.tpe } } - if (settings.debug.value) { - println(indent + "glb of " + ts + " at depth "+depth)//debug - indent = indent + " " - } + // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG val res = if (depth < 0) NothingClass.tpe else glb0(ts) - if (settings.debug.value) { - indent = indent.substring(0, indent.length() - 2) - log(indent + "glb of " + ts + " is " + res)//debug - } + // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG if (ts exists (_.isNotNull)) res.notNull else res } @@ -4726,7 +4720,7 @@ A type's typeSymbol should never be inspected directly. * of types. */ private def commonOwner(tps: List[Type]): Symbol = { - if (settings.debug.value) log("computing common owner of types " + tps)//debug + // if (settings.debug.value) log("computing common owner of types " + tps)//DEBUG commonOwnerMap.init tps foreach { tp => commonOwnerMap.apply(tp); () } commonOwnerMap.result @@ -4793,7 +4787,7 @@ A type's typeSymbol should never be inspected directly. */ def addMember(thistp: Type, tp: Type, sym: Symbol) { assert(sym != NoSymbol) - if (settings.debug.value) log("add member " + sym+":"+sym.info+" to "+thistp) + // if (settings.debug.value) log("add member " + sym+":"+sym.info+" to "+thistp) //DEBUG if (!(thistp specializes sym)) { if (sym.isTerm) for (alt <- tp.nonPrivateDecl(sym.name).alternatives) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index b3e4666f35..596c11bcac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -481,8 +481,7 @@ trait Contexts { self: Analyzer => } impls } - if (settings.debug.value) - log("collect implicit imports " + imp + "=" + collect(imp.tree.selectors))//debug + //if (settings.debug.value) log("collect implicit imports " + imp + "=" + collect(imp.tree.selectors))//DEBUG collect(imp.tree.selectors) } @@ -499,17 +498,14 @@ trait Contexts { self: Analyzer => val newImplicits: List[ImplicitInfo] = if (owner != nextOuter.owner && owner.isClass && !owner.isPackageClass && !inSelfSuperCall) { if (!owner.isInitialized) return nextOuter.implicitss - if (settings.debug.value) - log("collect member implicits " + owner + ", implicit members = " + - owner.thisType.implicitMembers)//debug + // if (settings.debug.value) log("collect member implicits " + owner + ", implicit members = " + owner.thisType.implicitMembers)//DEBUG val savedEnclClass = enclClass this.enclClass = this val res = collectImplicits(owner.thisType.implicitMembers, owner.thisType) this.enclClass = savedEnclClass res } else if (scope != nextOuter.scope && !owner.isPackageClass) { - if (settings.debug.value) - log("collect local implicits " + scope.toList)//debug + if (settings.debug.value) log("collect local implicits " + scope.toList)//DEBUG collectImplicits(scope.toList, NoPrefix) } else if (imports != nextOuter.imports) { assert(imports.tail == nextOuter.imports) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index d4a7b9e1e7..e36e8ffb02 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -615,7 +615,7 @@ trait Namers { self: Analyzer => clazz.typeOfThis = selfTypeCompleter(self.tpt) self.symbol = clazz.thisSym.setPos(self.pos) } else { - self.tpt.tpe = NoType + self.tpt defineType NoType if (self.name != nme.WILDCARD) { clazz.typeOfThis = clazz.tpe self.symbol = clazz.thisSym @@ -747,7 +747,7 @@ trait Namers { self: Analyzer => var vparamSymss = enterValueParams(meth, vparamss) if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt.tpe = context.enclClass.owner.tpe + tpt defineType context.enclClass.owner.tpe tpt setPos meth.pos.focus } @@ -873,7 +873,7 @@ trait Namers { self: Analyzer => var pfs = resultPt.paramTypes for (vparam <- vparams) { if (vparam.tpt.isEmpty) { - vparam.tpt.tpe = pfs.head + vparam.tpt defineType pfs.head vparam.tpt setPos vparam.pos.focus vparam.symbol setInfo pfs.head } @@ -900,7 +900,7 @@ trait Namers { self: Analyzer => } for (vparams <- vparamss; vparam <- vparams if vparam.tpt.isEmpty) { context.error(vparam.pos, "missing parameter type") - vparam.tpt.tpe = ErrorType + vparam.tpt defineType ErrorType } addDefaultGetters(meth, vparamss, tparams, overriddenSymbol) @@ -910,7 +910,7 @@ trait Namers { self: Analyzer => // replace deSkolemized symbols with skolemized ones (for resultPt computed by looking at overridden symbol, right?) val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) // compute result type from rhs - tpt.tpe = widenIfNotFinal(meth, typer.computeType(rhs, pt), pt) + tpt defineType widenIfNotFinal(meth, typer.computeType(rhs, pt), pt) tpt setPos meth.pos.focus tpt.tpe } else typer.typedType(tpt).tpe @@ -1148,7 +1148,7 @@ trait Namers { self: Analyzer => context.error(tpt.pos, "missing parameter type"); ErrorType } else { - tpt.tpe = widenIfNotFinal( + tpt defineType widenIfNotFinal( sym, newTyper(typer1.context.make(vdef, sym)).computeType(rhs, WildcardType), WildcardType) diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java index b1cc464513..bd38e37503 100644 --- a/src/library/scala/runtime/BoxesRunTime.java +++ b/src/library/scala/runtime/BoxesRunTime.java @@ -117,59 +117,71 @@ public class BoxesRunTime /* COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON ... COMPARISON */ + private static int eqTypeCode(Number a) { + if ((a instanceof Integer) || (a instanceof Byte)) return INT; + if (a instanceof Long) return LONG; + if (a instanceof Double) return DOUBLE; + if (a instanceof Short) return INT; + if (a instanceof Float) return FLOAT; + return OTHER; + } + + public static boolean equals(Object x, Object y) { + if (x == y) return true; + if (x == null) return false; + return equals2(x, y); + } + /** Since all applicable logic has to be present in the equals method of a ScalaNumber * in any case, we dispatch to it as soon as we spot one on either side. */ - public static boolean equals(Object x, Object y) { + public static boolean equals2(Object x, Object y) { if (x instanceof Number) { - if (x instanceof ScalaNumber) - return x.equals(y); - Number xn = (Number)x; - if (y instanceof Number) { - if (y instanceof ScalaNumber) - return y.equals(x); + if (y instanceof Number) { Number yn = (Number)y; - if ((xn instanceof Double) || (yn instanceof Double)) - return xn.doubleValue() == yn.doubleValue(); - if ((xn instanceof Float) || (yn instanceof Float)) - return xn.floatValue() == yn.floatValue(); - if ((xn instanceof Long) || (yn instanceof Long)) - return xn.longValue() == yn.longValue(); - if (typeCode(x) <= INT && typeCode(y) <= INT) + int xcode = eqTypeCode(xn); + int ycode = eqTypeCode(yn); + switch (ycode > xcode ? ycode : xcode) { + case INT: return xn.intValue() == yn.intValue(); - - return x.equals(y); - } - if (y instanceof Character) + 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.equals(y); + return xc.charValue() == ((Character)y).charValue(); if (y instanceof Number) return equalsNumChar((Number)y, xc); - } else if (x == null) { - return y == null; } return x.equals(y); } - private static boolean equalsNumChar(Number x, Character y) { - char ch = y.charValue(); - if (x instanceof Double) - return x.doubleValue() == ch; - if (x instanceof Float) - return x.floatValue() == ch; - if (x instanceof Long) - return x.longValue() == ch; - if (x instanceof ScalaNumber) - return x.equals(y); - if (typeCode(x) <= INT) - return x.intValue() == ch; - - return x.equals(y); + private static boolean equalsNumChar(Number xn, Character yc) { + char ch = yc.charValue(); + switch (eqTypeCode(xn)) { + case INT: + return xn.intValue() == ch; + case LONG: + return xn.longValue() == ch; + case FLOAT: + return xn.floatValue() == ch; + case DOUBLE: + return xn.doubleValue() == ch; + default: + return xn.equals(yc); + } } /** Hashcode algorithm is driven by the requirements imposed diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index a93ff61a41..6467751a1f 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -123,8 +123,9 @@ object ScalaRunTime { /** Fast path equality method for inlining; used when -optimise is set. */ @inline def inlinedEquals(x: Object, y: Object): Boolean = - if (x eq null) y eq null - else if (x.isInstanceOf[Number] || x.isInstanceOf[Character]) BoxesRunTime.equals(x, y) + if (x eq y) true + else if (x eq null) false + else if (x.isInstanceOf[Number] || x.isInstanceOf[Character]) BoxesRunTime.equals2(x, y) else x.equals(y) def _equals(x: Product, y: Any): Boolean = y match { diff --git a/test/files/pos/t2484.scala b/test/files/pos/t2484.scala new file mode 100755 index 0000000000..6990c46099 --- /dev/null +++ b/test/files/pos/t2484.scala @@ -0,0 +1,17 @@ +class Admin extends javax.swing.JApplet { + val jScrollPane = new javax.swing.JScrollPane (null, 0, 0) + def bug2484: Unit = { + scala.concurrent.ops.spawn {jScrollPane.synchronized { + def someFunction () = {} + //scala.concurrent.ops.spawn {someFunction ()} + jScrollPane.addComponentListener (new java.awt.event.ComponentAdapter {override def componentShown (e: java.awt.event.ComponentEvent) = { + someFunction (); jScrollPane.removeComponentListener (this)}}) + }} + } +} +// t2630.scala +object Test { + def meh(xs: List[Any]) { + xs map { x => (new AnyRef {}) } + } +} -- cgit v1.2.3