Skip to content

Commit 88041ac

Browse files
committed
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.
1 parent 20c6285 commit 88041ac

File tree

1 file changed

+106
-66
lines changed

1 file changed

+106
-66
lines changed

src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 106 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import config.Printers.pickling
2222
class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
2323
import TastyFormat._
2424
import TastyName._
25+
import TreeUnpickler._
2526
import tpd._
2627

2728
private var readPositions = false
@@ -45,18 +46,12 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
4546
private val treeAtAddr = new mutable.HashMap[Addr, Tree]
4647
private val typeAtAddr = new mutable.HashMap[Addr, Type] // currently populated only for types that are known to be SHAREd.
4748

48-
// Currently disabled set used for checking that all
49-
// already encountered symbols are forward refereneces. This
50-
// check fails in more complicated scenarios of separate
51-
// compilation in dotty (for instance: compile all of `core`
52-
// given the TASTY files of everything else in the compiler).
53-
// I did not have the time to track down what caused the failure.
54-
// The testing scheme could well have produced a false negative.
55-
//
56-
// private var stubs: Set[Symbol] = Set()
57-
49+
/** The root symbol denotation which are defined by the Tasty file associated with this
50+
* TreeUnpickler. Set by `enterTopLevel`.
51+
*/
5852
private var roots: Set[SymDenotation] = null
5953

54+
/** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */
6055
private var ownerTree: OwnerTree = _
6156

6257
private def registerSym(addr: Addr, sym: Symbol) = {
@@ -69,7 +64,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
6964
*/
7065
def enterTopLevel(roots: Set[SymDenotation])(implicit ctx: Context): Unit = {
7166
this.roots = roots
72-
new TreeReader(reader).fork.indexStats(reader.endAddr)
67+
var rdr = new TreeReader(reader).fork
68+
ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
69+
rdr.indexStats(reader.endAddr)
7370
}
7471

7572
/** The unpickled trees */
@@ -123,17 +120,19 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
123120
/** Record all directly nested definitions and templates in current tree
124121
* as `OwnerTree`s in `buf`
125122
*/
126-
def scanTree(buf: ListBuffer[OwnerTree]): Unit = {
123+
def scanTree(buf: ListBuffer[OwnerTree], mode: MemberDefMode = AllDefs): Unit = {
127124
val start = currentAddr
128125
val tag = readByte()
129-
//println(s"scan tree at $currentAddr, tag = $tag")
130126
tag match {
131127
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | TEMPLATE =>
132128
val end = readEnd()
133129
for (i <- 0 until numRefs(tag)) readNat()
134-
buf += new OwnerTree(start, fork, end)
130+
if (tag == TEMPLATE) scanTrees(buf, end, MemberDefsOnly)
131+
if (mode != NoMemberDefs) buf += new OwnerTree(start, tag, fork, end)
132+
goto(end)
135133
case tag =>
136-
if (tag >= firstLengthTreeTag) {
134+
if (mode == MemberDefsOnly) skipTree(tag)
135+
else if (tag >= firstLengthTreeTag) {
137136
val end = readEnd()
138137
var nrefs = numRefs(tag)
139138
if (nrefs < 0) {
@@ -154,8 +153,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
154153
/** Record all directly nested definitions and templates between current address and `end`
155154
* as `OwnerTree`s in `buf`
156155
*/
157-
def scanTrees(buf: ListBuffer[OwnerTree], end: Addr): Unit = {
158-
while (currentAddr.index < end.index) scanTree(buf)
156+
def scanTrees(buf: ListBuffer[OwnerTree], end: Addr, mode: MemberDefMode): Unit = {
157+
while (currentAddr.index < end.index) scanTree(buf, mode)
159158
assert(currentAddr.index == end.index)
160159
}
161160

@@ -197,19 +196,25 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
197196
until(end) { readNat(); readType().asInstanceOf[T] }
198197

199198
/** Read referece to definition and return symbol created at that definition */
200-
def readSymRef()(implicit ctx: Context): Symbol = {
201-
val start = currentAddr
202-
val addr = readAddr()
203-
symAtAddr get addr match {
204-
case Some(sym) => sym
205-
case None =>
206-
// Create a stub; owner might be wrong but will be overwritten later.
207-
forkAt(addr).createSymbol()
208-
val sym = symAtAddr(addr)
209-
ctx.log(i"forward reference to $sym")
210-
// stubs += sym
211-
sym
212-
}
199+
def readSymRef()(implicit ctx: Context): Symbol = symbolAt(readAddr())
200+
201+
/** The symbol at given address; createa new one if none exists yet */
202+
def symbolAt(addr: Addr)(implicit ctx: Context): Symbol = symAtAddr.get(addr) match {
203+
case Some(sym) =>
204+
sym
205+
case None =>
206+
val sym = forkAt(addr).createSymbol()(ctx.withOwner(ownerTree.findOwner(addr)))
207+
ctx.log(i"forward reference to $sym")
208+
sym
209+
}
210+
211+
/** The symbol defined by current definition */
212+
def symbolAtCurrent()(implicit ctx: Context): Symbol = symAtAddr.get(currentAddr) match {
213+
case Some(sym) =>
214+
assert(ctx.owner == sym.owner, i"owner discrepancy for $sym, expected: ${ctx.owner}, found: ${sym.owner}")
215+
sym
216+
case None =>
217+
createSymbol()
213218
}
214219

215220
/** Read a type */
@@ -414,7 +419,21 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
414419
/** Create symbol of definition node and enter in symAtAddr map
415420
* @return the created symbol
416421
*/
417-
def createSymbol()(implicit ctx: Context): Symbol = {
422+
def createSymbol()(implicit ctx: Context): Symbol = nextByte match {
423+
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
424+
createMemberSymbol()
425+
case TEMPLATE =>
426+
val localDummy = ctx.newLocalDummy(ctx.owner)
427+
registerSym(currentAddr, localDummy)
428+
localDummy
429+
case tag =>
430+
throw new Error(s"illegal createSymbol at $currentAddr, tag = $tag")
431+
}
432+
433+
/** Create symbol of member definition or parameter node and enter in symAtAddr map
434+
* @return the created symbol
435+
*/
436+
def createMemberSymbol()(implicit ctx: Context): Symbol = {
418437
val start = currentAddr
419438
val tag = readByte()
420439
val end = readEnd()
@@ -451,29 +470,18 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
451470
case _ =>
452471
val completer = adjustIfModule(new Completer(subReader(start, end)))
453472
if (isClass)
454-
ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer,
455-
privateWithin, coord = start.index)
456-
else {
457-
val sym = symAtAddr.get(start) match {
458-
case Some(preExisting) =>
459-
//assert(stubs contains preExisting, preExisting)
460-
//stubs -= preExisting
461-
preExisting
462-
case none =>
463-
ctx.newNakedSymbol(start.index)
464-
}
465-
val denot = ctx.SymDenotation(symbol = sym, owner = ctx.owner, name, flags, completer, privateWithin)
466-
sym.denot = denot
467-
sym
468-
}
469-
} // TODO set position
473+
ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord = start.index)
474+
else
475+
ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord = start.index)
476+
} // TODO set position somehow (but take care not to upset Symbol#isDefinedInCurrentRun)
470477
sym.annotations = annots
471478
ctx.enter(sym)
472479
registerSym(start, sym)
473480
if (isClass) {
474481
sym.completer.withDecls(newScope)
475482
forkAt(templateStart).indexTemplateParams()(localContext(sym))
476483
}
484+
goto(start)
477485
sym
478486
}
479487

@@ -551,7 +559,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
551559
while (currentAddr.index < end.index) {
552560
nextByte match {
553561
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM =>
554-
val sym = createSymbol()
562+
val sym = symbolAtCurrent()
563+
skipTree()
555564
if (sym.isTerm && !sym.is(MethodOrLazyOrDeferred))
556565
initsFlags = EmptyFlags
557566
else if (sym.isClass ||
@@ -586,7 +595,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
586595
* `tag` starting at current address.
587596
*/
588597
def indexParams(tag: Int)(implicit ctx: Context) =
589-
while (nextByte == tag) createSymbol()
598+
while (nextByte == tag) {
599+
symbolAtCurrent()
600+
skipTree()
601+
}
590602

591603
/** Create symbols for all type and value parameters of template starting
592604
* at current address.
@@ -613,7 +625,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
613625
val end = readEnd()
614626

615627
def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = {
616-
fork.indexParams(tag)
628+
fork.indexParams(tag)(localContext(sym))
617629
readIndexedParams(tag)
618630
}
619631

@@ -704,8 +716,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
704716
TermRef.withSig(cls.owner.thisType, cls.name.sourceModuleName, Signature.NotAMethod)
705717
else NoType
706718
setClsInfo(Nil, assumedSelfType)
707-
val localDummy = ctx.newLocalDummy(cls)
708-
registerSym(start, localDummy)
719+
val localDummy = symbolAtCurrent()
709720
assert(readByte() == TEMPLATE)
710721
val end = readEnd()
711722
val tparams = readIndexedParams[TypeDef](TYPEPARAM)
@@ -758,7 +769,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
758769
}
759770

760771
def readTopLevel()(implicit ctx: Context): List[Tree] = {
761-
ownerTree = new OwnerTree(NoAddr, fork, reader.endAddr)
762772
@tailrec def read(acc: ListBuffer[Tree]): List[Tree] = nextByte match {
763773
case IMPORT | PACKAGE =>
764774
acc += readIndexedStat(NoSymbol)
@@ -1010,32 +1020,62 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
10101020
}
10111021
}
10121022

1013-
class OwnerTree(val addr: Addr, reader: TreeReader, val end: Addr) {
1023+
/** A lazy datastructure that records how definitions are nested in TASTY data.
1024+
* The structure is lazy because it needs to be computed only for forward references
1025+
* to symbols that happen before the referenced symbol is created (see `symbolAt`).
1026+
* Such forward references are rare.
1027+
*
1028+
* @param addr The address of tree representing an owning definition, NoAddr for root tree
1029+
* @param tag The tag at `addr`. Used to determine which subtrees to scan for children
1030+
* (i.e. if `tag` is template, don't scan member defs, as these belong already
1031+
* to enclosing class).
1032+
* @param reader The reader to be used for scanning for children
1033+
* @param end The end of the owning definition
1034+
*/
1035+
class OwnerTree(val addr: Addr, tag: Int, reader: TreeReader, val end: Addr) {
1036+
1037+
/** All definitions that have the definition at `addr` as closest enclosing definition */
10141038
lazy val children: List[OwnerTree] = {
10151039
val buf = new ListBuffer[OwnerTree]
1016-
reader.scanTrees(buf, end)
1040+
reader.scanTrees(buf, end, if (tag == TEMPLATE) NoMemberDefs else AllDefs)
10171041
buf.toList
10181042
}
1043+
1044+
/** Find the owner of definition at `addr` */
10191045
def findOwner(addr: Addr)(implicit ctx: Context): Symbol = {
1020-
//println(s"find owner $addr")
1021-
def search(cs: List[OwnerTree], current: Symbol): Symbol = cs match {
1046+
def search(cs: List[OwnerTree], current: Symbol): Symbol =
1047+
try cs match {
10221048
case ot :: cs1 =>
1023-
if (ot.addr.index == addr.index) {
1024-
//println(i"search ok $addr, owner = $current")
1049+
if (ot.addr.index == addr.index)
10251050
current
1026-
}
1027-
else if (ot.addr.index < addr.index && addr.index < ot.end.index) {
1028-
val encl = reader.symbolAt(ot.addr)
1029-
//println(s"search $addr in ${ot.children} with $encl")
1030-
search(ot.children, encl)
1031-
}
1051+
else if (ot.addr.index < addr.index && addr.index < ot.end.index)
1052+
search(ot.children, reader.symbolAt(ot.addr))
10321053
else
10331054
search(cs1, current)
10341055
case Nil =>
1035-
throw new Error("unattached tree")
1056+
throw new TreeWithoutOwner
1057+
}
1058+
catch {
1059+
case ex: TreeWithoutOwner =>
1060+
println(i"no owner for $addr among $cs") // DEBUG
1061+
throw ex
10361062
}
10371063
search(children, NoSymbol)
10381064
}
1065+
10391066
override def toString = s"OwnerTree(${addr.index}, ${end.index}"
10401067
}
10411068
}
1069+
1070+
object TreeUnpickler {
1071+
1072+
/** An enumeration indicating which subtrees should be added to an OwnerTree. */
1073+
type MemberDefMode = Int
1074+
final val MemberDefsOnly = 0 // add only member defs; skip other statements
1075+
final val NoMemberDefs = 1 // add only statements that are not member defs
1076+
final val AllDefs = 2 // add everything
1077+
1078+
class TreeWithoutOwner extends Exception
1079+
}
1080+
1081+

0 commit comments

Comments
 (0)