Skip to content

Commit 18fddc1

Browse files
committed
Fixes for TypePositions scheme
Notable, deal with the fact that type trees can be shared, so the map from trees to positioned types must be a multi-map.
1 parent 3aadd24 commit 18fddc1

File tree

5 files changed

+163
-108
lines changed

5 files changed

+163
-108
lines changed

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

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,18 @@ import core._
1111
import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._
1212
import collection.mutable
1313
import TastyBuffer._
14+
import TreeBuffer._
15+
import ast.TypePositions.PosAnnot
1416
import util.Positions._
1517

16-
class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr]) {
18+
class PositionPickler(pickler: TastyPickler, addrsInfo: AddrsInfo) {
1719
val buf = new TastyBuffer(5000)
1820
pickler.newSection("Positions", buf)
1921
import buf._
2022
import ast.tpd._
21-
22-
private val remainingAddrs = new java.util.IdentityHashMap[Tree, Iterator[Addr]]
23-
24-
def nextTreeAddr(tree: Tree): Option[Addr] = remainingAddrs.get(tree) match {
25-
case null =>
26-
addrsOfTree(tree) match {
27-
case Nil =>
28-
None
29-
case addr :: Nil =>
30-
Some(addr)
31-
case addrs =>
32-
remainingAddrs.put(tree, addrs.iterator)
33-
nextTreeAddr(tree)
34-
}
35-
case it: Iterator[_] =>
36-
if (it.hasNext) Some(it.next) else None
37-
}
23+
24+
private val addrOfIterator = addrsInfo.addrs.iterator
25+
private val positionedOfIterator = addrsInfo.positioned.iterator
3826

3927
def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean, hasPoint: Boolean) = {
4028
def toInt(b: Boolean) = if (b) 1 else 0
@@ -65,24 +53,34 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr]
6553
case _ => false
6654
}
6755

56+
def picklePos(ad: Addressed, pos: Position) =
57+
if (addrOfIterator.hasNext(ad)) {
58+
val addr = addrOfIterator.next(ad)
59+
//println(i"pickling $ad with $pos at $addr")
60+
pickleDeltas(addr.index, pos)
61+
}
62+
6863
def traverse(x: Any): Unit = x match {
6964
case x: Tree @unchecked =>
70-
val pos = if (x.isInstanceOf[MemberDef]) x.pos else x.pos.toSynthetic
71-
if (pos.exists && (pos != x.initialPos.toSynthetic || alwaysNeedsPos(x))) {
72-
nextTreeAddr(x) match {
73-
case Some(addr) =>
74-
//println(i"pickling $x with $pos at $addr")
75-
pickleDeltas(addr.index, pos)
65+
if (positionedOfIterator.hasNext(x)) {
66+
val posType = positionedOfIterator.next(x)
67+
//println(s"pickle type pos $posType")
68+
posType.foreachPart {
69+
case t @ AnnotatedType(_, PosAnnot(pos)) => picklePos(t, pos.toSynthetic)
7670
case _ =>
77-
//println(i"no address for $x")
78-
}
71+
}
7972
}
80-
//else if (x.pos.exists) println(i"skipping $x")
81-
x match {
82-
case x: MemberDef @unchecked => traverse(x.symbol.annotations.map(_.tree))
83-
case _ =>
73+
else {
74+
val pos = if (x.isInstanceOf[MemberDef]) x.pos else x.pos.toSynthetic
75+
if (pos.exists && (pos != x.initialPos.toSynthetic || alwaysNeedsPos(x)))
76+
picklePos(x, pos)
77+
//else if (x.pos.exists) println(i"skipping $x")
78+
x match {
79+
case x: MemberDef @unchecked => traverse(x.symbol.annotations.map(_.tree))
80+
case _ =>
81+
}
82+
traverse(x.productIterator)
8483
}
85-
traverse(x.productIterator)
8684
case xs: TraversableOnce[_] =>
8785
xs.foreach(traverse)
8886
case _ =>

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

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,102 @@ import ast.untpd.Tree
1010
import ast.TypePositions.PosAnnot
1111
import Types.Type
1212

13-
class TreeBuffer extends TastyBuffer(50000) {
13+
object TreeBuffer {
14+
15+
/** A set of iterators indexed by a key */
16+
abstract class MultiIterator[Key, Value] {
17+
def hasNext(key: Key): Boolean
18+
def next(key: Key): Value
19+
}
20+
21+
/** A map from `Key`s to lists of `Value`s. Optimized for the case
22+
* where only a single value is associated with a key.
23+
*/
24+
class MultiMap[Key <: AnyRef, Value] {
25+
private type Values = Any // really: Value | List[Values]
26+
private val m = new java.util.IdentityHashMap[Key, Values]
27+
28+
/** Register `key -> value` binding as last value for `key`. */
29+
def register(key: Key, value: Value) =
30+
m.put(key,
31+
m.get(key) match {
32+
case null => value
33+
case vs: List[_] => vs :+ value
34+
case v => v :: value :: Nil
35+
})
36+
37+
/** The list of values associated with to `key` */
38+
def apply(key: Key): List[Value] = m.get(key) match {
39+
case null => Nil
40+
case vs: List[Value] => vs
41+
case v: Value => v :: Nil
42+
}
43+
44+
/** Transform all values stored in the map using `op` */
45+
def updateValues(op: Value => Value): Unit = {
46+
val it = m.keySet.iterator
47+
while (it.hasNext) {
48+
val x = it.next()
49+
m.put(x,
50+
m.get(x) match {
51+
case vs: List[Value] => vs.map(op)
52+
case v: Value => op(v)
53+
})
54+
}
55+
}
56+
57+
/** A new multi-iterator over this map */
58+
def iterator = new MultiIterator[Key, Value] {
59+
private val remaining = new java.util.IdentityHashMap[Key, Iterator[Value]]
60+
def hasNext(key: Key) = remaining.get(key) match {
61+
case it: Iterator[_] => it.hasNext
62+
case _ => m.get(key) != null
63+
}
64+
def next(key: Key) = remaining.get(key) match {
65+
case it: Iterator[_] => it.next
66+
case _ =>
67+
m.get(key) match {
68+
case null =>
69+
Iterator.empty.next
70+
case (v: Value) :: (vs: List[Value]) =>
71+
remaining.put(key, if (vs.isEmpty) Iterator.empty else vs.iterator)
72+
v
73+
case v: Value =>
74+
remaining.put(key, Iterator.empty)
75+
v
76+
}
77+
}
78+
}
79+
}
80+
81+
/** The type of things that have addresses of type `Addr`.
82+
* This is really `Tree | Type`, but that can't be expressed in current Scala
83+
*/
84+
type Addressed = AnyRef
85+
86+
/** Address info associated with a TreeBuffer, consisting of two multi-maps,
87+
* one from trees and types to addresses, and
88+
* one from trees to positioned types.
89+
*/
90+
trait AddrsInfo {
91+
def addrs: MultiMap[Addressed, Addr]
92+
def positioned: MultiMap[Tree, Type]
93+
}
94+
}
95+
96+
class TreeBuffer extends TastyBuffer(50000) with TreeBuffer.AddrsInfo {
97+
import TreeBuffer._
1498

1599
private final val ItemsOverOffsets = 2
16100
private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets)
17101
private var offsets = new Array[Int](initialOffsetSize)
18102
private var isRelative = new Array[Boolean](initialOffsetSize)
19103
private var delta: Array[Int] = _
20104
private var numOffsets = 0
21-
22-
private type TreeAddrs = Any // really: Addr | List[Addr]
23-
24-
private type Addressed = Any // really: Tree | Positioned
25-
26-
/** A map from trees to the address(es) at which a tree is pickled. There may be several
27-
* such addresses if the tree is shared. To keep the map compact, the value type is a
28-
* disjunction of a single address (which is the common case) and a list of addresses.
29-
*/
30-
private val treeAddrs = new java.util.IdentityHashMap[Addressed, TreeAddrs]
31-
32-
def registerTreeAddr(tree: Tree) =
33-
treeAddrs.put(tree,
34-
treeAddrs.get(tree) match {
35-
case null => currentAddr
36-
case x: Addr => x :: currentAddr :: Nil
37-
case xs: List[_] => xs :+ currentAddr
38-
})
39-
40-
def addrsOfTree(tree: Tree): List[Addr] = treeAddrs.get(tree) match {
41-
case null => Nil
42-
case addr: Addr => addr :: Nil
43-
case addrs: List[Addr] => addrs
44-
}
45-
46-
val posTypeOfTree = new java.util.IdentityHashMap[Tree, Type]
47105

106+
val addrs = new MultiMap[Addressed, Addr]
107+
val positioned = new MultiMap[Tree, Type]
108+
48109
private def offset(i: Int): Addr = Addr(offsets(i))
49110

50111
private def keepOffset(relative: Boolean): Unit = {
@@ -168,16 +229,7 @@ class TreeBuffer extends TastyBuffer(50000) {
168229
wasted
169230
}
170231

171-
def adjustTreeAddrs(): Unit = {
172-
val it = treeAddrs.keySet.iterator
173-
while (it.hasNext) {
174-
val tree = it.next
175-
treeAddrs.get(tree) match {
176-
case addr: Addr => treeAddrs.put(tree, adjusted(addr))
177-
case addrs: List[Addr] => treeAddrs.put(tree, addrs.map(adjusted))
178-
}
179-
}
180-
}
232+
def adjustTreeAddrs(): Unit = addrs.updateValues(adjusted)
181233

182234
/** Final assembly, involving the following steps:
183235
* - compute deltas

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

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class TreePickler(pickler: TastyPickler) {
143143

144144
def pickleType(tpe0: Type, richTypes: Boolean = false)(implicit ctx: Context): Unit = tpe0 match {
145145
case AnnotatedType(tpe1, PosAnnot(pos)) =>
146+
addrs.register(tpe0, currentAddr)
146147
pickleType(tpe1, richTypes)
147148
case _ =>
148149
try {
@@ -164,7 +165,7 @@ class TreePickler(pickler: TastyPickler) {
164165
}
165166

166167
def pickleTypeWithPos(tpe: Type, tree: Tree)(implicit ctx: Context): Unit = {
167-
registerTreeAddr(tree)
168+
addrs.register(tree, currentAddr)
168169
pickleType(tpe)
169170
}
170171

@@ -315,34 +316,33 @@ class TreePickler(pickler: TastyPickler) {
315316
pickled
316317
}
317318

318-
def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = tpt match {
319-
case _: TypeTree => pickleTypeWithPos(tpt.tpe, tpt)
320-
case _ =>
321-
val posType = TypePositions.toType(tpt)
322-
323-
def checkTypePositions() = {
324-
val tree = TypePositions.toTree(posType)
325-
val origRefs = TypePositions.refPositions(tpt)
326-
val newRefs = TypePositions.refPositions(tree)
327-
assert(TypePositions.refPositions(tpt).toSet subsetOf TypePositions.refPositions(tree).toSet,
328-
i"""$tpt
329-
|-->
330-
|$posType
331-
|-->
332-
|$tree
333-
|/
334-
|${posType.toString}
335-
|/
336-
|${tree.toString}""")
319+
/** Pickle type tree. This will pickle its type but will try to keep position
320+
* info with it.
321+
*/
322+
def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit =
323+
picklePosTypeOfTree(TypePositions.toType(tpt), tpt)
324+
325+
/** Pickle positioned type associated with type tree `tpt`. */
326+
private def picklePosTypeOfTree(posType: Type, tpt: Tree)(implicit ctx: Context): Unit = {
327+
def checkTypePositions() = {
328+
val tree = TypePositions.toTree(posType)
329+
val origRefs = TypePositions.refPositions(tpt)
330+
val newRefs = TypePositions.refPositions(tree)
331+
assert(TypePositions.refPositions(tpt).toSet subsetOf TypePositions.refPositions(tree).toSet,
332+
i"""$tpt
333+
|-->
334+
|$posType
335+
|-->
336+
|$tree
337+
|/
338+
|${posType.toString}
339+
|/
340+
|${tree.toString}""")
337341

338-
}
339-
if (false) checkTypePositions
340-
341-
posType match {
342-
case pd @ AnnotatedType(_, _: PosAnnot) => posTypeOfTree.put(tpt, pd)
343-
case _ =>
344-
}
345-
pickleTypeWithPos(posType, tpt)
342+
}
343+
if (false) checkTypePositions
344+
positioned.register(tpt, posType)
345+
pickleType(posType)
346346
}
347347

348348
def pickleTreeUnlessEmpty(tree: Tree)(implicit ctx: Context): Unit =
@@ -365,7 +365,7 @@ class TreePickler(pickler: TastyPickler) {
365365
}
366366

367367
def pickleParam(tree: Tree)(implicit ctx: Context): Unit = {
368-
registerTreeAddr(tree)
368+
addrs.register(tree, currentAddr)
369369
tree match {
370370
case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt)
371371
case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs)
@@ -384,7 +384,7 @@ class TreePickler(pickler: TastyPickler) {
384384
}
385385

386386
def pickleTree(tree: Tree)(implicit ctx: Context): Unit = try {
387-
registerTreeAddr(tree)
387+
addrs.register(tree, currentAddr)
388388
tree match {
389389
case tree if tree.isType =>
390390
pickleTpt(tree)
@@ -528,13 +528,12 @@ class TreePickler(pickler: TastyPickler) {
528528
if ((selfInfo ne NoType) || !tree.self.isEmpty) {
529529
writeByte(SELFDEF)
530530
pickleName(tree.self.name)
531-
if (!tree.self.isEmpty) registerTreeAddr(tree.self.tpt)
532-
pickleType {
533-
cinfo.selfInfo match {
534-
case sym: Symbol => sym.info
535-
case tp: Type => tp
536-
}
531+
val selfTp = cinfo.selfInfo match {
532+
case sym: Symbol => sym.info
533+
case tp: Type => tp
537534
}
535+
if (!tree.self.tpt.isEmpty) pickleTpt(tree.self.tpt)
536+
else pickleType(selfTp)
538537
}
539538
pickleStats(tree.constr :: rest)
540539
}
@@ -561,7 +560,7 @@ class TreePickler(pickler: TastyPickler) {
561560
}
562561

563562
def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = {
564-
registerTreeAddr(id)
563+
addrs.register(id, currentAddr)
565564
writeByte(tag)
566565
pickleName(id.name)
567566
}

src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Decorators._
1515
import config.Config
1616
import scala.annotation.switch
1717
import language.implicitConversions
18+
import dotty.tools.dotc.ast.TypePositions
1819

1920
class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
2021

@@ -529,7 +530,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
529530
txt = toText(tree.typeOpt)
530531
if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) {
531532
val pos =
532-
if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic
533+
if (homogenizedView)
534+
tree match {
535+
case _: MemberDef => tree.pos // point is important for MemberDefs since it points to defined id
536+
case SingletonTypeTree(ref) if ref.pos.exists => ref.pos.toSynthetic // we only serialize pos of underlying ref
537+
case _ => tree.pos.toSynthetic // deserialized position is synthetic
538+
}
533539
else tree.pos
534540
val clsStr = "" // DEBUG: if (tree.isType) tree.getClass.toString else ""
535541
txt = (txt ~ "@" ~ pos.toString ~ clsStr).close

src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ class Pickler extends Phase {
4646
val treePkl = pickler.treePkl
4747
treePkl.pickle(tree :: Nil)
4848
treePkl.compactify()
49-
pickler.addrsOfTree = treePkl.buf.addrsOfTree
49+
pickler.addrsOfTree = treePkl.buf.addrs.apply
5050
pickler.addrOfSym = treePkl.addrOfSym
5151
if (tree.pos.exists)
52-
new PositionPickler(pickler, treePkl.buf.addrsOfTree).picklePositions(tree :: Nil)
52+
new PositionPickler(pickler, treePkl.buf).picklePositions(tree :: Nil)
5353

5454
def rawBytes = // not needed right now, but useful to print raw format.
5555
pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {

0 commit comments

Comments
 (0)