Skip to content

Commit 042615f

Browse files
committed
Fix position pickler
- align conditions for generating sourdce changes with new scheme for assigning source files to trees - Also: avoid context capture in Thicket
1 parent f9fb06e commit 042615f

File tree

5 files changed

+52
-26
lines changed

5 files changed

+52
-26
lines changed

compiler/src/dotty/tools/dotc/ast/Positioned.scala

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ abstract class Positioned(implicit @transientParam src: SourceInfo) extends Prod
3333
def sourcePos(implicit ctx: Context): SourcePosition = source.atPos(pos)
3434

3535
setId(TreeIds.nextIdFor(initialFile(src)))
36-
setPos(initialPos, srcfile)
36+
setPos(initialSpan(), srcfile)
3737

3838
protected def setId(id: Int): Unit = {
3939
myUniqueId = id
@@ -156,7 +156,7 @@ abstract class Positioned(implicit @transientParam src: SourceInfo) extends Prod
156156
newpd
157157
}
158158

159-
def initialFile(src: SourceInfo): AbstractFile = {
159+
def elemsFile: AbstractFile = {
160160
def firstFile(x: Any): AbstractFile = x match {
161161
case x: Positioned if x.pos.exists =>
162162
x.srcfile
@@ -167,38 +167,53 @@ abstract class Positioned(implicit @transientParam src: SourceInfo) extends Prod
167167
null
168168
}
169169
def firstElemFile(n: Int): AbstractFile =
170-
if (n == productArity) src.source.file
170+
if (n == productArity) null
171171
else {
172172
val f = firstFile(productElement(n))
173173
if (f != null) f else firstElemFile(n + 1)
174174
}
175175
firstElemFile(0)
176176
}
177177

178-
/** The initial, synthetic position. This is usually the union of all positioned children's positions.
178+
private def initialFile(src: SourceInfo): AbstractFile = {
179+
val f = elemsFile
180+
if (f != null) f else src.srcfile
181+
}
182+
183+
/** The initial, synthetic span. This is usually the union of all positioned children's positions.
184+
* @param ignoreTypeTrees If true, TypeTree children are not counted for the span.
185+
* This is important for predicting whether a position entry is
186+
* needed for pickling, since TypeTrees are pickled as types, so
187+
* their position is lost.
179188
*/
180-
def initialPos: Position = {
189+
def initialSpan(ignoreTypeTrees: Boolean = false): Position = {
190+
191+
def include(p1: Position, p2: Positioned) = p2 match {
192+
case _: Trees.TypeTree[_] if ignoreTypeTrees => p1
193+
case _ => p1.union(p2.pos)
194+
}
195+
196+
def includeAll(pos: Position, xs: List[_]): Position = xs match {
197+
case (p: Positioned) :: xs1 if sameSource(p) => includeAll(include(pos, p), xs1)
198+
case (xs0: List[_]) :: xs1 => includeAll(includeAll(pos, xs0), xs1)
199+
case _ :: xs1 => includeAll(pos, xs1)
200+
case _ => pos
201+
}
202+
181203
var n = productArity
182204
var pos = NoPosition
183205
while (n > 0) {
184206
n -= 1
185207
productElement(n) match {
186-
case p: Positioned if sameSource(p) => pos = pos union p.pos
187-
case m: untpd.Modifiers => pos = unionPos(unionPos(pos, m.mods), m.annotations)
188-
case xs: List[_] => pos = unionPos(pos, xs)
208+
case p: Positioned if sameSource(p) => pos = include(pos, p)
209+
case m: untpd.Modifiers => pos = includeAll(includeAll(pos, m.mods), m.annotations)
210+
case xs: List[_] => pos = includeAll(pos, xs)
189211
case _ =>
190212
}
191213
}
192214
pos.toSynthetic
193215
}
194216

195-
private def unionPos(pos: Position, xs: List[_]): Position = xs match {
196-
case (p: Positioned) :: xs1 if sameSource(p) => unionPos(pos union p.pos, xs1)
197-
case (xs0: List[_]) :: xs1 => unionPos(unionPos(pos, xs0), xs1)
198-
case _ :: xs1 => unionPos(pos, xs1)
199-
case _ => pos
200-
}
201-
202217
private def sameSource(that: Positioned) = srcfile == that.srcfile
203218

204219
def contains(that: Positioned): Boolean = {

compiler/src/dotty/tools/dotc/ast/TreeIds.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ object TreeIds {
1616
@sharable private[this] val counters = new ConcurrentHashMap[AbstractFile, AtomicInteger]
1717
@sharable private[this] val fileOfChunk = mutable.ArrayBuffer[AbstractFile]()
1818

19-
def nextId(implicit src: SourceInfo): Int = nextIdFor(src.source.file)
19+
def nextId(implicit src: SourceInfo): Int = nextIdFor(src.srcfile)
2020

2121
def nextIdFor(file: AbstractFile): Int = {
2222
var ctr = counters.get(file)

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ object Trees {
598598
case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])(implicit @transientParam src: SourceInfo)
599599
extends Tree[T] {
600600
type ThisTree[-T >: Untyped] = Inlined[T]
601-
override def initialPos: Position = call.pos
601+
override def initialSpan(ignoreTypeTrees: Boolean): Position = call.pos
602602
}
603603

604604
/** A type tree that represents an existing or inferred type */
@@ -790,7 +790,7 @@ object Trees {
790790
if (trees eq newTrees)
791791
this
792792
else
793-
Thicket[T](newTrees).asInstanceOf[this.type]
793+
Thicket[T](newTrees)(SourceInfo(srcfile)).asInstanceOf[this.type]
794794
}
795795

796796
override def foreachInThicket(op: Tree[T] => Unit): Unit =

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import config.Settings._
2020
import config.Config
2121
import reporting._
2222
import reporting.diagnostic.Message
23-
import io.{AbstractFile, PlainFile, Path}
23+
import io.{AbstractFile, NoAbstractFile, PlainFile, Path}
2424
import scala.io.Codec
2525
import collection.mutable
2626
import printing._
@@ -38,8 +38,12 @@ import plugins._
3838
object Contexts {
3939

4040
trait SourceInfo {
41-
def source: SourceFile
42-
def withSource(file: AbstractFile): SourceInfo
41+
def srcfile: AbstractFile
42+
}
43+
object SourceInfo {
44+
def apply(src: AbstractFile) = new SourceInfo {
45+
def srcfile = src
46+
}
4347
}
4448

4549
private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]()
@@ -169,6 +173,8 @@ object Contexts {
169173
protected def source_=(source: SourceFile): Unit = _source = source
170174
def source: SourceFile = _source
171175

176+
def srcfile = source.file
177+
172178
/** A map in which more contextual properties can be stored
173179
* Typically used for attributes that are read and written only in special situations.
174180
*/

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
4848
buf.writeInt(pickler.nameBuffer.nameIndex(source.path.toTermName).index)
4949
}
5050

51-
/** True if x's position shouldn't be reconstructed automatically from its initialPos
51+
/** True if x's position shouldn't be reconstructed automatically from its initial span
5252
*/
5353
def alwaysNeedsPos(x: Positioned) = x match {
5454
case
55-
// initialPos is inaccurate for trees with lazy field
55+
// initialSpan is inaccurate for trees with lazy field
5656
_: WithLazyField[_]
5757

5858
// A symbol is created before the corresponding tree is unpickled,
5959
// and its position cannot be changed afterwards.
60-
// so we cannot use the tree initialPos to set the symbol position.
60+
// so we cannot use the tree initialSpan to set the symbol position.
6161
// Instead, we always pickle the position of definitions.
6262
| _: Trees.DefTree[_]
6363

@@ -70,8 +70,13 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
7070
case x: untpd.Tree =>
7171
var sourceFile = current
7272
val pos = if (x.isInstanceOf[untpd.MemberDef]) x.pos else x.pos.toSynthetic
73-
val sourceChange = x.source.file != current
74-
if (pos.exists && (pos != x.initialPos.toSynthetic || sourceChange || alwaysNeedsPos(x))) {
73+
val sourceChange =
74+
x.source.file != x.elemsFile ||
75+
x.elemsFile == null && x.source.file != current
76+
if (pos.exists && (
77+
pos != x.initialSpan(ignoreTypeTrees = true).toSynthetic ||
78+
sourceChange ||
79+
alwaysNeedsPos(x))) {
7580
addrOfTree(x) match {
7681
case Some(addr) if !pickledIndices.contains(addr.index) || sourceChange =>
7782
// we currently do not share trees when unpickling, so if one path to a tree contains

0 commit comments

Comments
 (0)