aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-05-22 21:55:53 +0200
committerMartin Odersky <odersky@gmail.com>2016-05-23 12:01:40 +0200
commitc204a962d32bc6ee64256501d7dfe6efabeccc9f (patch)
tree9cb448c693faf12fe22ace26511a928d033799b2 /src
parent7f08c1fbd674d6ebf3f74e75fe5b3e83daf9da7e (diff)
downloaddotty-c204a962d32bc6ee64256501d7dfe6efabeccc9f.tar.gz
dotty-c204a962d32bc6ee64256501d7dfe6efabeccc9f.tar.bz2
dotty-c204a962d32bc6ee64256501d7dfe6efabeccc9f.zip
Adopt new scheme for handling forward references in Tasty
Instead of stubbing with potentially wrong owners and hping for the best, we now compute owners on demand, using the lazy data structure of an OwnerTree.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala172
1 files changed, 106 insertions, 66 deletions
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index a82c4cad4..9598fc0c5 100644
--- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -22,6 +22,7 @@ import config.Printers.pickling
class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
import TastyFormat._
import TastyName._
+ import TreeUnpickler._
import tpd._
private var readPositions = false
@@ -45,18 +46,12 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
private val treeAtAddr = new mutable.HashMap[Addr, Tree]
private val typeAtAddr = new mutable.HashMap[Addr, Type] // currently populated only for types that are known to be SHAREd.
- // Currently disabled set used for checking that all
- // already encountered symbols are forward refereneces. This
- // check fails in more complicated scenarios of separate
- // compilation in dotty (for instance: compile all of `core`
- // given the TASTY files of everything else in the compiler).
- // I did not have the time to track down what caused the failure.
- // The testing scheme could well have produced a false negative.
- //
- // private var stubs: Set[Symbol] = Set()
-
+ /** The root symbol denotation which are defined by the Tasty file associated with this
+ * TreeUnpickler. Set by `enterTopLevel`.
+ */
private var roots: Set[SymDenotation] = null
+ /** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */
private var ownerTree: OwnerTree = _
private def registerSym(addr: Addr, sym: Symbol) = {
@@ -69,7 +64,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
*/
def enterTopLevel(roots: Set[SymDenotation])(implicit ctx: Context): Unit = {
this.roots = roots
- new TreeReader(reader).fork.indexStats(reader.endAddr)
+ var rdr = new TreeReader(reader).fork
+ ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
+ rdr.indexStats(reader.endAddr)
}
/** The unpickled trees */
@@ -123,17 +120,19 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
/** Record all directly nested definitions and templates in current tree
* as `OwnerTree`s in `buf`
*/
- def scanTree(buf: ListBuffer[OwnerTree]): Unit = {
+ def scanTree(buf: ListBuffer[OwnerTree], mode: MemberDefMode = AllDefs): Unit = {
val start = currentAddr
val tag = readByte()
- //println(s"scan tree at $currentAddr, tag = $tag")
tag match {
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | TEMPLATE =>
val end = readEnd()
for (i <- 0 until numRefs(tag)) readNat()
- buf += new OwnerTree(start, fork, end)
+ if (tag == TEMPLATE) scanTrees(buf, end, MemberDefsOnly)
+ if (mode != NoMemberDefs) buf += new OwnerTree(start, tag, fork, end)
+ goto(end)
case tag =>
- if (tag >= firstLengthTreeTag) {
+ if (mode == MemberDefsOnly) skipTree(tag)
+ else if (tag >= firstLengthTreeTag) {
val end = readEnd()
var nrefs = numRefs(tag)
if (nrefs < 0) {
@@ -154,8 +153,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
/** Record all directly nested definitions and templates between current address and `end`
* as `OwnerTree`s in `buf`
*/
- def scanTrees(buf: ListBuffer[OwnerTree], end: Addr): Unit = {
- while (currentAddr.index < end.index) scanTree(buf)
+ def scanTrees(buf: ListBuffer[OwnerTree], end: Addr, mode: MemberDefMode): Unit = {
+ while (currentAddr.index < end.index) scanTree(buf, mode)
assert(currentAddr.index == end.index)
}
@@ -197,19 +196,25 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
until(end) { readNat(); readType().asInstanceOf[T] }
/** Read referece to definition and return symbol created at that definition */
- def readSymRef()(implicit ctx: Context): Symbol = {
- val start = currentAddr
- val addr = readAddr()
- symAtAddr get addr match {
- case Some(sym) => sym
- case None =>
- // Create a stub; owner might be wrong but will be overwritten later.
- forkAt(addr).createSymbol()
- val sym = symAtAddr(addr)
- ctx.log(i"forward reference to $sym")
- // stubs += sym
- sym
- }
+ def readSymRef()(implicit ctx: Context): Symbol = symbolAt(readAddr())
+
+ /** The symbol at given address; createa new one if none exists yet */
+ def symbolAt(addr: Addr)(implicit ctx: Context): Symbol = symAtAddr.get(addr) match {
+ case Some(sym) =>
+ sym
+ case None =>
+ val sym = forkAt(addr).createSymbol()(ctx.withOwner(ownerTree.findOwner(addr)))
+ ctx.log(i"forward reference to $sym")
+ sym
+ }
+
+ /** The symbol defined by current definition */
+ def symbolAtCurrent()(implicit ctx: Context): Symbol = symAtAddr.get(currentAddr) match {
+ case Some(sym) =>
+ assert(ctx.owner == sym.owner, i"owner discrepancy for $sym, expected: ${ctx.owner}, found: ${sym.owner}")
+ sym
+ case None =>
+ createSymbol()
}
/** Read a type */
@@ -414,7 +419,21 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
/** Create symbol of definition node and enter in symAtAddr map
* @return the created symbol
*/
- def createSymbol()(implicit ctx: Context): Symbol = {
+ def createSymbol()(implicit ctx: Context): Symbol = nextByte match {
+ case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
+ createMemberSymbol()
+ case TEMPLATE =>
+ val localDummy = ctx.newLocalDummy(ctx.owner)
+ registerSym(currentAddr, localDummy)
+ localDummy
+ case tag =>
+ throw new Error(s"illegal createSymbol at $currentAddr, tag = $tag")
+ }
+
+ /** Create symbol of member definition or parameter node and enter in symAtAddr map
+ * @return the created symbol
+ */
+ def createMemberSymbol()(implicit ctx: Context): Symbol = {
val start = currentAddr
val tag = readByte()
val end = readEnd()
@@ -451,22 +470,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
case _ =>
val completer = adjustIfModule(new Completer(subReader(start, end)))
if (isClass)
- ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer,
- privateWithin, coord = start.index)
- else {
- val sym = symAtAddr.get(start) match {
- case Some(preExisting) =>
- //assert(stubs contains preExisting, preExisting)
- //stubs -= preExisting
- preExisting
- case none =>
- ctx.newNakedSymbol(start.index)
- }
- val denot = ctx.SymDenotation(symbol = sym, owner = ctx.owner, name, flags, completer, privateWithin)
- sym.denot = denot
- sym
- }
- } // TODO set position
+ ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord = start.index)
+ else
+ ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord = start.index)
+ } // TODO set position somehow (but take care not to upset Symbol#isDefinedInCurrentRun)
sym.annotations = annots
ctx.enter(sym)
registerSym(start, sym)
@@ -474,6 +481,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
sym.completer.withDecls(newScope)
forkAt(templateStart).indexTemplateParams()(localContext(sym))
}
+ goto(start)
sym
}
@@ -551,7 +559,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
while (currentAddr.index < end.index) {
nextByte match {
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
- val sym = createSymbol()
+ val sym = symbolAtCurrent()
+ skipTree()
if (sym.isTerm && !sym.is(MethodOrLazyOrDeferred))
initsFlags = EmptyFlags
else if (sym.isClass ||
@@ -586,7 +595,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
* `tag` starting at current address.
*/
def indexParams(tag: Int)(implicit ctx: Context) =
- while (nextByte == tag) createSymbol()
+ while (nextByte == tag) {
+ symbolAtCurrent()
+ skipTree()
+ }
/** Create symbols for all type and value parameters of template starting
* at current address.
@@ -613,7 +625,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
val end = readEnd()
def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = {
- fork.indexParams(tag)
+ fork.indexParams(tag)(localContext(sym))
readIndexedParams(tag)
}
@@ -704,8 +716,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
TermRef.withSig(cls.owner.thisType, cls.name.sourceModuleName, Signature.NotAMethod)
else NoType
setClsInfo(Nil, assumedSelfType)
- val localDummy = ctx.newLocalDummy(cls)
- registerSym(start, localDummy)
+ val localDummy = symbolAtCurrent()
assert(readByte() == TEMPLATE)
val end = readEnd()
val tparams = readIndexedParams[TypeDef](TYPEPARAM)
@@ -758,7 +769,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
}
def readTopLevel()(implicit ctx: Context): List[Tree] = {
- ownerTree = new OwnerTree(NoAddr, fork, reader.endAddr)
@tailrec def read(acc: ListBuffer[Tree]): List[Tree] = nextByte match {
case IMPORT | PACKAGE =>
acc += readIndexedStat(NoSymbol)
@@ -1010,32 +1020,62 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
}
}
- class OwnerTree(val addr: Addr, reader: TreeReader, val end: Addr) {
+ /** A lazy datastructure that records how definitions are nested in TASTY data.
+ * The structure is lazy because it needs to be computed only for forward references
+ * to symbols that happen before the referenced symbol is created (see `symbolAt`).
+ * Such forward references are rare.
+ *
+ * @param addr The address of tree representing an owning definition, NoAddr for root tree
+ * @param tag The tag at `addr`. Used to determine which subtrees to scan for children
+ * (i.e. if `tag` is template, don't scan member defs, as these belong already
+ * to enclosing class).
+ * @param reader The reader to be used for scanning for children
+ * @param end The end of the owning definition
+ */
+ class OwnerTree(val addr: Addr, tag: Int, reader: TreeReader, val end: Addr) {
+
+ /** All definitions that have the definition at `addr` as closest enclosing definition */
lazy val children: List[OwnerTree] = {
val buf = new ListBuffer[OwnerTree]
- reader.scanTrees(buf, end)
+ reader.scanTrees(buf, end, if (tag == TEMPLATE) NoMemberDefs else AllDefs)
buf.toList
}
+
+ /** Find the owner of definition at `addr` */
def findOwner(addr: Addr)(implicit ctx: Context): Symbol = {
- //println(s"find owner $addr")
- def search(cs: List[OwnerTree], current: Symbol): Symbol = cs match {
+ def search(cs: List[OwnerTree], current: Symbol): Symbol =
+ try cs match {
case ot :: cs1 =>
- if (ot.addr.index == addr.index) {
- //println(i"search ok $addr, owner = $current")
+ if (ot.addr.index == addr.index)
current
- }
- else if (ot.addr.index < addr.index && addr.index < ot.end.index) {
- val encl = reader.symbolAt(ot.addr)
- //println(s"search $addr in ${ot.children} with $encl")
- search(ot.children, encl)
- }
+ else if (ot.addr.index < addr.index && addr.index < ot.end.index)
+ search(ot.children, reader.symbolAt(ot.addr))
else
search(cs1, current)
case Nil =>
- throw new Error("unattached tree")
+ throw new TreeWithoutOwner
+ }
+ catch {
+ case ex: TreeWithoutOwner =>
+ println(i"no owner for $addr among $cs") // DEBUG
+ throw ex
}
search(children, NoSymbol)
}
+
override def toString = s"OwnerTree(${addr.index}, ${end.index}"
}
}
+
+object TreeUnpickler {
+
+ /** An enumeration indicating which subtrees should be added to an OwnerTree. */
+ type MemberDefMode = Int
+ final val MemberDefsOnly = 0 // add only member defs; skip other statements
+ final val NoMemberDefs = 1 // add only statements that are not member defs
+ final val AllDefs = 2 // add everything
+
+ class TreeWithoutOwner extends Exception
+}
+
+