|
| 1 | +package dotty.tools.dotc |
| 2 | +package transform |
| 3 | + |
| 4 | +import core._ |
| 5 | +import Flags._, Symbols._, Contexts._, Types._, Scopes._ |
| 6 | +import util.HashSet |
| 7 | +import collection.mutable.HashMap |
| 8 | +import collection.immutable.BitSet |
| 9 | +import scala.annotation.tailrec |
| 10 | + |
| 11 | +/** A class that yields a kind of iterator (`Cursor`), |
| 12 | + * which yields all pairs of overriding/overridden symbols |
| 13 | + * that are visible in some baseclass, unless there's a parent class |
| 14 | + * that already contains the same pairs. |
| 15 | + * |
| 16 | + * Adapted from the 2.9 version of OverridingPairs. The 2.10 version is IMO |
| 17 | + * way too unwieldy to be maintained. |
| 18 | + */ |
| 19 | +abstract class OverridingPairs { |
| 20 | + |
| 21 | + /** The cursor class |
| 22 | + * @param base the base class that contains the overriding pairs |
| 23 | + */ |
| 24 | + class Cursor(base: Symbol)(implicit ctx: Context) { |
| 25 | + |
| 26 | + private val self = base.thisType |
| 27 | + |
| 28 | + /** Symbols to exclude: Here these are constructors and private locals. |
| 29 | + * But it may be refined in subclasses. |
| 30 | + */ |
| 31 | + protected def exclude(sym: Symbol): Boolean = |
| 32 | + sym.isConstructor || sym.is(PrivateLocal) |
| 33 | + |
| 34 | + /** The parents of base (may also be refined). |
| 35 | + */ |
| 36 | + protected def parents: Array[Symbol] = base.info.parents.toArray map (_.typeSymbol) |
| 37 | + |
| 38 | + /** Does `sym1` match `sym2` so that it qualifies as overriding. |
| 39 | + * Types always match. Term symbols match if their membertypes |
| 40 | + * relative to <base>.this do |
| 41 | + */ |
| 42 | + protected def matches(sym1: Symbol, sym2: Symbol): Boolean = |
| 43 | + sym1.isType || { |
| 44 | + val info1 = self.memberInfo(sym1) |
| 45 | + val info2 = self.memberInfo(sym2) |
| 46 | + // info1.signature == info2.signature && // TODO enable for speed |
| 47 | + info1 matches info2 |
| 48 | + } |
| 49 | + |
| 50 | + /** The symbols that can take part in an overriding pair */ |
| 51 | + private val decls = { |
| 52 | + val decls = newScope |
| 53 | + // fill `decls` with overriding shadowing overridden */ |
| 54 | + def fillDecls(bcs: List[Symbol], deferred: Boolean): Unit = bcs match { |
| 55 | + case bc :: bcs1 => |
| 56 | + fillDecls(bcs1, deferred) |
| 57 | + var e = bc.info.decls.asInstanceOf[MutableScope].lastEntry |
| 58 | + while (e != null) { |
| 59 | + if (e.sym.is(Deferred) == deferred && !exclude(e.sym)) |
| 60 | + decls.enter(e.sym) |
| 61 | + e = e.prev |
| 62 | + } |
| 63 | + case nil => |
| 64 | + } |
| 65 | + // first, deferred (this will need to change if we change lookup rules! |
| 66 | + fillDecls(base.info.baseClasses, deferred = true) |
| 67 | + // then, concrete. |
| 68 | + fillDecls(base.info.baseClasses, deferred = false) |
| 69 | + decls |
| 70 | + } |
| 71 | + |
| 72 | + private val subParents = { |
| 73 | + val subParents = new HashMap[Symbol, BitSet] |
| 74 | + for (bc <- base.info.baseClasses) |
| 75 | + subParents(bc) = BitSet(parents.indices.filter(parents(_).derivesFrom(bc)): _*) |
| 76 | + subParents |
| 77 | + } |
| 78 | + |
| 79 | + private def hasCommonParentAsSubclass(cls1: Symbol, cls2: Symbol): Boolean = |
| 80 | + (subParents(cls1) intersect subParents(cls2)).isEmpty |
| 81 | + |
| 82 | + /** The scope entries that have already been visited as overridden |
| 83 | + * (maybe excluded because of hasCommonParentAsSubclass). |
| 84 | + * These will not appear as overriding |
| 85 | + */ |
| 86 | + private val visited = new HashSet[ScopeEntry](64) |
| 87 | + |
| 88 | + /** The current entry candidate for overriding |
| 89 | + */ |
| 90 | + private var curEntry = decls.lastEntry |
| 91 | + |
| 92 | + /** The current entry candidate for overridden */ |
| 93 | + private var nextEntry = curEntry |
| 94 | + |
| 95 | + /** The current candidate symbol for overriding */ |
| 96 | + var overriding: Symbol = _ |
| 97 | + |
| 98 | + /** If not null: The symbol overridden by overriding */ |
| 99 | + var overridden: Symbol = _ |
| 100 | + |
| 101 | + //@M: note that next is called once during object initialization |
| 102 | + def hasNext: Boolean = curEntry ne null |
| 103 | + |
| 104 | + @tailrec |
| 105 | + final def next: Unit = { |
| 106 | + if (curEntry ne null) { |
| 107 | + overriding = curEntry.sym |
| 108 | + if (nextEntry ne null) { |
| 109 | + val overridingOwner = overriding.owner |
| 110 | + do { |
| 111 | + do { |
| 112 | + nextEntry = decls.lookupNextEntry(nextEntry); |
| 113 | + /* DEBUG |
| 114 | + if ((nextEntry ne null) && |
| 115 | + !(nextEntry.sym hasFlag PRIVATE) && |
| 116 | + !(overriding.owner == nextEntry.sym.owner) && |
| 117 | + !matches(overriding, nextEntry.sym)) |
| 118 | + println("skipping "+overriding+":"+self.memberType(overriding)+overriding.locationString+" to "+nextEntry.sym+":"+self.memberType(nextEntry.sym)+nextEntry.sym.locationString) |
| 119 | + */ |
| 120 | + } while ((nextEntry ne null) && |
| 121 | + (//!!!!nextEntry.sym.canMatchInheritedSymbols || |
| 122 | + (overriding.owner == nextEntry.sym.owner) || |
| 123 | + (!matches(overriding, nextEntry.sym)) || |
| 124 | + (exclude(overriding)))) |
| 125 | + if (nextEntry ne null) visited.addEntry(nextEntry) |
| 126 | + // skip nextEntry if a class in `parents` is a subclass of the owners of both |
| 127 | + // overriding and nextEntry.sym |
| 128 | + } while ((nextEntry ne null) && |
| 129 | + hasCommonParentAsSubclass(overridingOwner, nextEntry.sym.owner)) |
| 130 | + if (nextEntry ne null) { |
| 131 | + overridden = nextEntry.sym; |
| 132 | + //Console.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG |
| 133 | + } else { |
| 134 | + do { |
| 135 | + curEntry = curEntry.prev |
| 136 | + } while ((curEntry ne null) && visited.contains(curEntry)) |
| 137 | + nextEntry = curEntry |
| 138 | + next |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + next |
| 145 | + } |
| 146 | +} |
0 commit comments