Skip to content

Commit 7d6e0aa

Browse files
committed
Make position pickling context-independent
1 parent 8ba1ea0 commit 7d6e0aa

File tree

5 files changed

+82
-56
lines changed

5 files changed

+82
-56
lines changed

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ import Contexts._, Symbols._, Annotations._, Decorators._
1616
import collection.mutable
1717
import util.Spans._
1818

19-
class PositionPickler(pickler: TastyPickler, addrOfTree: PositionPickler.TreeToAddr) {
19+
class PositionPickler(
20+
pickler: TastyPickler,
21+
addrOfTree: PositionPickler.TreeToAddr,
22+
treeAnnots: untpd.MemberDef => List[tpd.Tree]) {
23+
24+
import ast.tpd._
2025
val buf: TastyBuffer = new TastyBuffer(5000)
2126
pickler.newSection("Positions", buf)
22-
import ast.tpd._
2327

2428
private val pickledIndices = new mutable.BitSet
2529

@@ -28,7 +32,7 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: PositionPickler.TreeToA
2832
(addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint)
2933
}
3034

31-
def picklePositions(roots: List[Tree])(using Context): Unit = {
35+
def picklePositions(roots: List[Tree], warnings: mutable.ListBuffer[String]): Unit = {
3236
var lastIndex = 0
3337
var lastSpan = Span(0, 0)
3438
def pickleDeltas(index: Int, span: Span) = {
@@ -55,7 +59,7 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: PositionPickler.TreeToA
5559
val cwd = java.nio.file.Paths.get("").toAbsolutePath().normalize()
5660
try cwd.relativize(path)
5761
catch case _: IllegalArgumentException =>
58-
report.warning("Could not relativize path for pickling: " + originalPath)
62+
warnings += "Could not relativize path for pickling: " + originalPath
5963
originalPath
6064
else
6165
originalPath
@@ -100,10 +104,9 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: PositionPickler.TreeToA
100104
pickleDeltas(addr.index, x.span)
101105
}
102106
}
103-
x match {
104-
case x: untpd.MemberDef @unchecked => traverse(x.symbol.annotations, x.source)
107+
x match
108+
case x: untpd.MemberDef => traverse(treeAnnots(x), x.source)
105109
case _ =>
106-
}
107110
val limit = x.productArity
108111
var n = 0
109112
while (n < limit) {
@@ -113,8 +116,6 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: PositionPickler.TreeToA
113116
case y :: ys =>
114117
traverse(y, current)
115118
traverse(ys, current)
116-
case x: Annotation =>
117-
traverse(x.tree, current)
118119
case _ =>
119120
}
120121
for (root <- roots)

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,11 @@ class TreeBuffer extends TastyBuffer(50000) {
177177
//println(s"offsets: ${offsets.take(numOffsets).deep}")
178178
//println(s"deltas: ${delta.take(numOffsets).deep}")
179179
var saved = 0
180-
while ({
180+
while
181181
saved = adjustDeltas()
182182
pickling.println(s"adjusting deltas, saved = $saved")
183183
saved > 0 && length / saved < 100
184-
})
185-
()
184+
do ()
186185
adjustOffsets()
187186
adjustTreeAddrs()
188187
val wasted = compress()

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

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import printing.Printer
1818
import printing.Texts._
1919
import util.SourceFile
2020
import annotation.constructorOnly
21+
import collection.mutable
2122

2223
object TreePickler {
2324

@@ -44,6 +45,15 @@ class TreePickler(pickler: TastyPickler) {
4445
private val forwardSymRefs = Symbols.newMutableSymbolMap[List[Addr]]
4546
private val pickledTypes = new java.util.IdentityHashMap[Type, Any] // Value type is really Addr, but that's not compatible with null
4647

48+
/** A list of annotation trees for every member definition, so that later
49+
* parallel position pickling does not need to access and force symbols.
50+
*/
51+
private val annotTrees = util.HashTable[untpd.MemberDef, mutable.ListBuffer[Tree]]()
52+
53+
def treeAnnots(tree: untpd.MemberDef): List[Tree] =
54+
val ts = annotTrees.lookup(tree)
55+
if ts == null then Nil else ts.toList
56+
4757
private def withLength(op: => Unit) = {
4858
val lengthAddr = reserveRef(relative = true)
4959
op
@@ -309,7 +319,8 @@ class TreePickler(pickler: TastyPickler) {
309319
if (!tree.isEmpty) pickleTree(tree)
310320
}
311321

312-
def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(using Context): Unit = {
322+
def pickleDef(tag: Int, mdef: MemberDef, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ())(using Context): Unit = {
323+
val sym = mdef.symbol
313324
assert(symRefs(sym) == NoAddr, sym)
314325
registerDef(sym)
315326
writeByte(tag)
@@ -321,16 +332,16 @@ class TreePickler(pickler: TastyPickler) {
321332
case _ if tpt.isType => pickleTpt(tpt)
322333
}
323334
pickleTreeUnlessEmpty(rhs)
324-
pickleModifiers(sym)
335+
pickleModifiers(sym, mdef)
325336
}
326337
}
327338

328339
def pickleParam(tree: Tree)(using Context): Unit = {
329340
registerTreeAddr(tree)
330341
tree match {
331-
case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt)
332-
case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs)
333-
case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs)
342+
case tree: ValDef => pickleDef(PARAM, tree, tree.tpt)
343+
case tree: DefDef => pickleDef(PARAM, tree, tree.tpt, tree.rhs)
344+
case tree: TypeDef => pickleDef(TYPEPARAM, tree, tree.rhs)
334345
}
335346
}
336347

@@ -520,7 +531,7 @@ class TreePickler(pickler: TastyPickler) {
520531
patterns.foreach(pickleTree)
521532
}
522533
case tree: ValDef =>
523-
pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs)
534+
pickleDef(VALDEF, tree, tree.tpt, tree.rhs)
524535
case tree: DefDef =>
525536
def pickleParamss(paramss: List[List[ValDef]]): Unit = paramss match
526537
case Nil =>
@@ -531,9 +542,9 @@ class TreePickler(pickler: TastyPickler) {
531542
def pickleAllParams =
532543
pickleParams(tree.tparams)
533544
pickleParamss(tree.vparamss)
534-
pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams)
545+
pickleDef(DEFDEF, tree, tree.tpt, tree.rhs, pickleAllParams)
535546
case tree: TypeDef =>
536-
pickleDef(TYPEDEF, tree.symbol, tree.rhs)
547+
pickleDef(TYPEDEF, tree, tree.rhs)
537548
case tree: Template =>
538549
registerDef(tree.symbol)
539550
writeByte(TEMPLATE)
@@ -648,7 +659,7 @@ class TreePickler(pickler: TastyPickler) {
648659
pickleName(id.name)
649660
}
650661

651-
def pickleModifiers(sym: Symbol)(using Context): Unit = {
662+
def pickleModifiers(sym: Symbol, mdef: MemberDef)(using Context): Unit = {
652663
import Flags._
653664
var flags = sym.flags
654665
val privateWithin = sym.privateWithin
@@ -660,7 +671,7 @@ class TreePickler(pickler: TastyPickler) {
660671
if (flags.is(ParamAccessor) && sym.isTerm && !sym.isSetter)
661672
flags = flags &~ ParamAccessor // we only generate a tag for parameter setters
662673
pickleFlags(flags, sym.isTerm)
663-
sym.annotations.foreach(pickleAnnotation(sym, _))
674+
val annots = sym.annotations.foreach(pickleAnnotation(sym, mdef, _))
664675
}
665676

666677
def pickleFlags(flags: FlagSet, isTerm: Boolean)(using Context): Unit = {
@@ -723,12 +734,15 @@ class TreePickler(pickler: TastyPickler) {
723734
ann.symbol == defn.BodyAnnot // inline bodies are reconstituted automatically when unpickling
724735
}
725736

726-
def pickleAnnotation(owner: Symbol, ann: Annotation)(using Context): Unit = {
727-
if (!isUnpicklable(owner, ann)) {
737+
def pickleAnnotation(owner: Symbol, mdef: MemberDef, ann: Annotation)(using Context): Unit =
738+
if !isUnpicklable(owner, ann) then
728739
writeByte(ANNOTATION)
729740
withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) }
730-
}
731-
}
741+
var treeBuf = annotTrees.lookup(mdef)
742+
if treeBuf == null then
743+
treeBuf = new mutable.ListBuffer[Tree]
744+
annotTrees.enter(mdef, treeBuf)
745+
treeBuf += ann.tree
732746

733747
// ---- main entry points ---------------------------------------
734748

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import dotty.tools.dotc.core.tasty.TreePickler.Hole
1515
import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter }
1616
import dotty.tools.dotc.core.tasty.DottyUnpickler
1717
import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode
18+
import dotty.tools.dotc.report
1819

1920
import dotty.tools.tasty.TastyString
2021

2122
import scala.reflect.ClassTag
2223

2324
import scala.internal.quoted.Unpickler._
2425
import scala.quoted.QuoteContext
26+
import scala.collection.mutable
2527

2628
object PickledQuotes {
2729
import tpd._
@@ -167,8 +169,11 @@ object PickledQuotes {
167169
val treePkl = pickler.treePkl
168170
treePkl.pickle(tree :: Nil)
169171
treePkl.compactify()
170-
if (tree.span.exists)
171-
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil)
172+
if tree.span.exists then
173+
val positionWarnings = new mutable.ListBuffer[String]()
174+
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
175+
.picklePositions(tree :: Nil, positionWarnings)
176+
positionWarnings.foreach(report.warning(_))
172177

173178
val pickled = pickler.assembleParts()
174179
quotePickling.println(s"**** pickled quote\n${new TastyPrinter(pickled).printContents()}")

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

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,41 +51,48 @@ class Pickler extends Phase {
5151
val unit = ctx.compilationUnit
5252
pickling.println(i"unpickling in run ${ctx.runId}")
5353

54-
for {
54+
for
5555
cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree))
5656
tree <- sliceTopLevel(unit.tpdTree, cls)
57-
}
58-
{
57+
do
5958
val pickler = new TastyPickler(cls)
6059
if ctx.settings.YtestPickler.value then
6160
beforePickling(cls) = tree.show
6261
picklers(cls) = pickler
6362
val treePkl = pickler.treePkl
6463
treePkl.pickle(tree :: Nil)
65-
val pickledF = Future {
66-
treePkl.compactify()
67-
if tree.span.exists then
68-
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil)
69-
70-
if !ctx.settings.YdropComments.value then
71-
new CommentPickler(pickler, treePkl.buf.addrOfTree).pickleComment(tree)
72-
73-
val pickled = pickler.assembleParts()
74-
75-
def rawBytes = // not needed right now, but useful to print raw format.
76-
pickled.iterator.grouped(10).toList.zipWithIndex.map {
77-
case (row, i) => s"${i}0: ${row.mkString(" ")}"
78-
}
79-
80-
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
81-
if pickling ne noPrinter then
82-
pickling.synchronized {
83-
println(i"**** pickled info of $cls")
84-
println(new TastyPrinter(pickled).printContents())
85-
}
86-
pickled
87-
}(using ExecutionContext.global)
88-
def force(): Array[Byte] = Await.result(pickledF, Duration.Inf)
64+
val positionWarnings = new mutable.ListBuffer[String]()
65+
val parContext = ctx.fresh // TODO refine
66+
val pickledF = inContext(parContext) {
67+
Future {
68+
treePkl.compactify()
69+
if tree.span.exists then
70+
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
71+
.picklePositions(tree :: Nil, positionWarnings)
72+
73+
if !ctx.settings.YdropComments.value then
74+
new CommentPickler(pickler, treePkl.buf.addrOfTree).pickleComment(tree)
75+
76+
val pickled = pickler.assembleParts()
77+
78+
def rawBytes = // not needed right now, but useful to print raw format.
79+
pickled.iterator.grouped(10).toList.zipWithIndex.map {
80+
case (row, i) => s"${i}0: ${row.mkString(" ")}"
81+
}
82+
83+
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
84+
if pickling ne noPrinter then
85+
pickling.synchronized {
86+
println(i"**** pickled info of $cls")
87+
println(new TastyPrinter(pickled).printContents())
88+
}
89+
pickled
90+
}(using ExecutionContext.global)
91+
}
92+
def force(): Array[Byte] =
93+
val result = Await.result(pickledF, Duration.Inf)
94+
positionWarnings.foreach(report.warning(_))
95+
result
8996

9097
// Turn off parallelism because it lead to non-deterministic CI failures:
9198
// - https://github.com/lampepfl/dotty/runs/1029579877?check_suite_focus=true#step:10:967
@@ -98,7 +105,7 @@ class Pickler extends Phase {
98105
if !ctx.settings.YparallelPickler.value then force()
99106

100107
unit.pickled += (cls -> force)
101-
}
108+
end for
102109
}
103110

104111
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = {

0 commit comments

Comments
 (0)