diff options
7 files changed, 125 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index b36d82f741..016174615c 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -170,7 +170,7 @@ class Settings(error: String => Unit) { } override def equals(that: Any) = that match { - case s:Settings => + case s: Settings => assert(this.allSettings.length == s.allSettings.length) List.forall2 ( this.allSettings, @@ -334,7 +334,7 @@ class Settings(error: String => Unit) { if (value == default) Nil else List(name, value.toString) override def equals(that: Any) = that match { - case is:IntSetting => this.name == is.name && this.value == is.value + case is: Settings#IntSetting => this.name == is.name && this.value == is.value case _ => false } @@ -355,7 +355,7 @@ class Settings(error: String => Unit) { def unparse: List[String] = if (value) List(name) else Nil override def equals(that: Any) = that match { - case bs:BooleanSetting => this.name == bs.name && this.value == bs.value + case bs: Settings#BooleanSetting => this.name == bs.name && this.value == bs.value case _ => false } @@ -389,7 +389,7 @@ class Settings(error: String => Unit) { if (value == default) Nil else List(name, value) override def equals(that: Any) = that match { - case ss:StringSetting => this.name == ss.name && this.value == ss.value + case ss: Settings# StringSetting => this.name == ss.name && this.value == ss.value case _ => false } @@ -428,7 +428,7 @@ class Settings(error: String => Unit) { for (opt <- value) yield nameColon+opt override def equals(that: Any) = that match { - case mss:MultiStringSetting => + case mss: Settings# MultiStringSetting => this.name == mss.name && this.value.length == mss.value.length && List.forall2(this.value.sort(_<_), mss.value.sort(_<_))(_==_) @@ -480,7 +480,7 @@ class Settings(error: String => Unit) { if (value == default) Nil else List(name + ":" + value) override def equals(that: Any) = that match { - case cs:ChoiceSetting => this.name == cs.name && this.value == cs.value + case cs: Settings# ChoiceSetting => this.name == cs.name && this.value == cs.value case _ => false } @@ -566,7 +566,7 @@ class Settings(error: String => Unit) { List(name + ":" + phase) ::: args)) override def equals(that: Any) = that match { - case ps:PhasesSetting => + case ps: Settings#PhasesSetting => this.name == ps.name && this.value.length == ps.value.length && List.forall2(this.value.sort(_<_), ps.value.sort(_<_))(_==_) diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index 4e65e6deab..f6b3b3064c 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -468,6 +468,11 @@ trait Symbols { isClass && (isAnonymousClass || isRefinementClass || isLocal || !owner.isPackageClass && owner.isLocalClass) + /** Is this symbol a member of class `clazz' + */ + def isMemberOf(clazz: Symbol) = + clazz.info.member(name).alternatives contains this + /** A a member of class `base' is incomplete if * (1) it is declared deferred or * (2) it is abstract override and its super symbol in `base' is diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index d9aa3e89aa..15cad39db3 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -8,6 +8,7 @@ package scala.tools.nsc.transform import scala.tools.nsc.symtab.classfile.ClassfileConstants._ import scala.collection.mutable.{HashMap,ListBuffer} +import scala.collection.immutable.Set import scala.tools.nsc.util.Position import symtab._ import Flags._ @@ -816,8 +817,9 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { * with the erased type of <code>m1</code> in the template. * </p> */ - private def bridgeDefs(owner: Symbol): List[Tree] = { - //Console.println("computing bridges for " + owner)//DEBUG + private def bridgeDefs(owner: Symbol): (List[Tree], Set[Symbol]) = { + var toBeRemoved: Set[Symbol] = Set() + //println("computing bridges for " + owner)//DEBUG assert(phase == currentRun.erasurePhase) val site = owner.thisType val bridgesScope = newScope @@ -834,7 +836,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { val member = opc.overriding val other = opc.overridden //Console.println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString);//DEBUG - if (!atPhase(currentRun.explicitOuterPhase)(member.isDeferred)) { + if (atPhase(currentRun.explicitOuterPhase)(!member.isDeferred)) { val otpe = erasure(other.tpe); val bridgeNeeded = atPhase(phase.next) ( !(other.tpe =:= member.tpe) && @@ -853,6 +855,11 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { .setInfo(otpe); bridgeTarget(bridge) = member atPhase(phase.next) { owner.info.decls.enter(bridge) } + if (other.owner == owner) { + //println("bridge to same: "+other+other.locationString)//DEBUG + atPhase(phase.next) { owner.info.decls.unlink(other) } + toBeRemoved += other + } bridgesScope enter bridge bridges = atPhase(phase.next) { @@ -874,7 +881,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { } opc.next } - bridges + (bridges, toBeRemoved) } /* for (val bc <- site.baseClasses.tail; val other <- bc.info.decls.toList) { @@ -893,8 +900,9 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { def addBridges(stats: List[Tree], base: Symbol): List[Tree] = if (base.isTrait) stats else { - val bridges = bridgeDefs(base) - if (bridges.isEmpty) stats else stats ::: bridges + val (bridges, toBeRemoved) = bridgeDefs(base) + if (bridges.isEmpty) stats + else (stats remove (stat => toBeRemoved contains stat.symbol)) ::: bridges } /** <p> diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index ec125fa694..ce0d3c651d 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -987,6 +987,11 @@ abstract class Mixin extends InfoTransform { } // } } +/* //DEBUG + case Ident(name) if (name.toString == "xxx") => + println(tree+":"+tree.tpe+"/"+tree.tpe.baseClasses) + tree +*/ case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 3f56ae416a..9266515057 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -10,8 +10,10 @@ import collection.mutable.HashMap import symtab.Flags._ import util.HashSet -/** This abstract class ... - * +/** A class that yields a kind of iterator (`Cursor`), + * which yields all pairs of overriding/overridden symbols + * that are visible in some baseclass, unless there's a parent class + * that aleady contains the same pairs. * @author Martin Odersky * @version 1.0 */ @@ -20,18 +22,35 @@ abstract class OverridingPairs { val global: Global import global._ + /** The cursor class + * @param base the base class that contains the overriding pairs + */ class Cursor(base: Symbol) { private val self = base.thisType + /** Symbols to exclude: Here these are constructors, private locals, + * and bridges. But it may be refined in subclasses. + * + */ protected def exclude(sym: Symbol): Boolean = sym.isConstructor || sym.isPrivateLocal || sym.hasFlag(BRIDGE) + /** The parents of base (may also be refined). + */ protected def parents: List[Type] = base.info.parents + /** Does `sym1` match `sym2` so that it qualifies as overiding. + * Types always match. Term symbols match if their membertypes + * relative to <base>.this do + */ protected def matches(sym1: Symbol, sym2: Symbol): Boolean = sym1.isType || (self.memberType(sym1) matches self.memberType(sym2)) + /** An implementation of BitSets as arrays (maybe consider collection.BitSet + * for that?) The main purpose of this is to implement + * intersectionContainsElement efficiently. + */ private type BitSet = Array[Int] private def newBitSet(size: Int): BitSet = new Array((size + 31) >> 5) @@ -42,17 +61,23 @@ abstract class OverridingPairs { bs(nshifted) = bs(nshifted) | nmask } - private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, - n: Int): Boolean = - { + /** Implements `bs1 * bs2 * {0..n} != 0. + * Used in hasCommonParentAsSubclass */ + private def intersectionContainsElementLeq(bs1: BitSet, bs2: BitSet, n: Int): Boolean = { val nshifted = n >> 5 - val nmask = 1 << (n & 31); - ((List.range(0, nshifted) exists (i => (bs1(i) & bs2(i)) != 0)) || - ((bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0)) + val nmask = 1 << (n & 31) + var i = 0 + while (i < nshifted) { + if ((bs1(i) & bs2(i)) != 0) return true + i += 1 + } + (bs1(nshifted) & bs2(nshifted) & (nmask | nmask - 1)) != 0 } + /** The symbols that can take part in an overriding pair */ private val decls = newScope + // fill `decls` with overriding shadowing overridden */ { def fillDecls(bcs: List[Symbol], deferredflag: Int) { if (!bcs.isEmpty) { fillDecls(bcs.tail, deferredflag) @@ -64,12 +89,17 @@ abstract class OverridingPairs { } } } + // first, deferred (this wil need to change if we change lookup rules! fillDecls(base.info.baseClasses, DEFERRED) + // then, concrete. fillDecls(base.info.baseClasses, 0) } private val size = base.info.baseClasses.length + /** A map from baseclasses of <base> to ints, with smaller ints meansing lower in + * lineraizatuon order. + */ private val index = new HashMap[Symbol, Int] { var i = 0 @@ -79,6 +109,15 @@ abstract class OverridingPairs { } } + /** A mapping from all base class indices to a bitset + * which indicates whether parents are subclasses. + * + * i \in subParents(j) iff + * exists p \in parents, b \in baseClasses: + * i = index(p) + * j = index(b) + * p isSubClass b + */ private val subParents = new Array[BitSet](size) { for (i <- List.range(0, size)) @@ -89,21 +128,32 @@ abstract class OverridingPairs { } } - - private def hasCommonParent(sym1: Symbol, sym2: Symbol) = { - //assert(index.get(sym1.owner) != None, "" + base + " " + sym1 + " " + sym1.owner);//DEBUG - //assert(index.get(sym2.owner) != None, "" + base + " " + sym2 + " " + sym2.owner);//DEBUG + /** Do `sym1` and `sym2` have a common subclass in `parents`? + * In that case we do not follow their overriding pairs + */ + private def hasCommonParentAsSubclass(sym1: Symbol, sym2: Symbol) = { val index1 = index(sym1.owner) val index2 = index(sym2.owner) - val minindex = if (index1 < index2) index1 else index2 - intersectionContainsElementLeq(subParents(index1), subParents(index2), minindex) + intersectionContainsElementLeq(subParents(index1), subParents(index2), index1 min index2) } + /** The scope entries that have already been visited as overridden + * (maybe excluded because of hasCommonParentAsSubclass). + * These will not appear as overriding + */ private val visited = new HashSet[ScopeEntry](256) + + /** The current entry candidate for overriding + */ private var curEntry = decls.elems + + /** The current entry candidate for overridden */ private var nextEntry = curEntry + /** The current candidate symbol for overridding */ var overriding: Symbol = _ + + /** If not null: The symbol overridden by overridding */ var overridden: Symbol = _ //@M: note that next is called once during object initialisation @@ -114,28 +164,31 @@ abstract class OverridingPairs { overriding = curEntry.sym if (nextEntry ne null) { do { - nextEntry = decls.lookupNextEntry(nextEntry); - } while ((nextEntry ne null) && - ((nextEntry.sym hasFlag PRIVATE) || - (overriding.owner == nextEntry.sym.owner) || - (!matches(overriding, nextEntry.sym)) || - (hasCommonParent(overriding, nextEntry.sym)) || - (exclude(overriding)))) - } - if (nextEntry ne null) { - overridden = nextEntry.sym; - //Console.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG - visited addEntry nextEntry - } else { - do { - curEntry = curEntry.next - } while ((curEntry ne null) && (visited contains curEntry)); - nextEntry = curEntry - next + do { + nextEntry = decls.lookupNextEntry(nextEntry); + } while ((nextEntry ne null) && + ((nextEntry.sym hasFlag PRIVATE) || + (overriding.owner == nextEntry.sym.owner) || + (!matches(overriding, nextEntry.sym)) || + (exclude(overriding)))) + if (nextEntry ne null) visited addEntry nextEntry + // skip nextEntry if a class in `parents` is a subclass of the owners of both + // overriding and nextEntry.sym + } while ((nextEntry ne null) && (hasCommonParentAsSubclass(overriding, nextEntry.sym))) + if (nextEntry ne null) { + overridden = nextEntry.sym; + //Console.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG + } else { + do { + curEntry = curEntry.next + } while ((curEntry ne null) && (visited contains curEntry)); + nextEntry = curEntry + next + } } } } - next //@M: ATTN! this method gets called during construction! + next } } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 693fa4e63e..f3c2bc3122 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -202,6 +202,8 @@ abstract class UnCurry extends InfoTransform with TypingTransformers { * I.e. for the method's non-local return key, generate: * * throw new NonLocalReturnException(key, expr) + * todo: maybe clone a pre-existing exception instead? + * (but what to do about excaptions that miss their targets?) */ private def nonLocalReturnThrow(expr: Tree, meth: Symbol) = localTyper.typed { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index af010998b8..acee4132af 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -103,8 +103,11 @@ abstract class RefChecks extends InfoTransform { case List(MixinOverrideError(_, msg)) => unit.error(clazz.pos, msg) case MixinOverrideError(member, msg) :: others => - unit.error(clazz.pos, msg+";\n other members with override errors are: "+ - (others.map(_.member.name).filter(member.name != _).removeDuplicates mkString ", ")) + val others1 = others.map(_.member.name.decode).filter(member.name != _).removeDuplicates + unit.error( + clazz.pos, + msg+(if (others1.isEmpty) "" + else ";\n other members with override errors are: "+(others1 mkString ", "))) } } @@ -273,7 +276,7 @@ abstract class RefChecks extends InfoTransform { val opc = new overridingPairs.Cursor(clazz) while (opc.hasNext) { - //Console.println("overrides " + opc.overriding/* + ":" + opc.overriding.tpe*/ + opc.overriding.locationString + " " + opc.overridden/* + ":" + opc.overridden.tpe*/ + opc.overridden.locationString + opc.overridden.hasFlag(DEFERRED));//DEBUG + //Console.println(opc.overriding/* + ":" + opc.overriding.tpe*/ + " in "+opc.overriding.fullNameString + " overrides " + opc.overridden/* + ":" + opc.overridden.tpe*/ + " in "+opc.overridden.fullNameString + "/"+ opc.overridden.hasFlag(DEFERRED));//debug if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden); opc.next |