Skip to content

Commit 0747b49

Browse files
committed
Add source component to Positioned
1 parent 1d62624 commit 0747b49

File tree

8 files changed

+115
-91
lines changed

8 files changed

+115
-91
lines changed

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

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,78 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package ast
34

45
import util.Positions._
5-
import util.SourcePosition
6+
import util.{SourceFile, SourcePosition}
67
import core.Contexts.{Context, SourceInfo}
78
import core.Decorators._
89
import core.Flags.{JavaDefined, Extension}
910
import core.StdNames.nme
11+
import io.AbstractFile
12+
import annotation.transientParam
13+
import annotation.internal.sharable
1014

1115
/** A base class for things that have positions (currently: modifiers and trees)
1216
*/
13-
abstract class Positioned extends Product {
17+
abstract class Positioned(implicit @transientParam src: SourceInfo) extends Product with Cloneable {
1418

19+
/** A unique identifier. Among other things, used for determining the source file
20+
* component of the position.
21+
*/
22+
private var myUniqueId: Int = _
1523
private[this] var curPos: Position = _
1624

17-
setPos(initialPos)
18-
19-
/** The item's position.
20-
*/
25+
/** The item's position */
2126
def pos: Position = curPos
2227

23-
/** Destructively update `curPos` to given position. Also, set any missing
28+
/** item's id */
29+
def uniqueId: Int = myUniqueId
30+
31+
protected def srcfile: AbstractFile = TreeIds.fileOfId(uniqueId)
32+
def source(implicit ctx: Context): SourceFile = ctx.getSource(srcfile)
33+
def sourcePos(implicit ctx: Context): SourcePosition = source.atPos(pos)
34+
35+
setId(TreeIds.nextIdFor(initialFile(src)))
36+
setPos(initialPos, srcfile)
37+
38+
protected def setId(id: Int): Unit = myUniqueId = id
39+
40+
/** Destructively update `curPos` to given position. Also, set any missing
2441
* positions in children.
2542
*/
26-
protected def setPos(pos: Position): Unit = {
27-
setPosUnchecked(pos)
28-
if (pos.exists) setChildPositions(pos.toSynthetic)
43+
protected def setPos(pos: Position, file: AbstractFile): Unit = {
44+
setPosUnchecked(pos, file)
45+
if (pos.exists) setChildPositions(pos.toSynthetic, file)
2946
}
3047

31-
def cloned(implicit src: SourceInfo): Positioned
32-
3348
/** A positioned item like this one with the position set to `pos`.
3449
* if the positioned item is source-derived, a clone is returned.
3550
* If the positioned item is synthetic, the position is updated
3651
* destructively and the item itself is returned.
3752
*/
3853
def withPos(pos: Position)(implicit src: SourceInfo): this.type = {
39-
val newpd = if (pos == curPos || curPos.isSynthetic) this else cloned
40-
newpd.setPos(pos)
41-
newpd.asInstanceOf[this.type]
54+
val ownPos = this.pos
55+
val newpd: this.type = if (pos == ownPos || ownPos.isSynthetic) this else cloneIn(srcfile)
56+
newpd.setPos(pos, srcfile)
57+
newpd
58+
}
59+
60+
def withPos(posd: Positioned)(implicit ctx: Context): this.type = {
61+
val ownPos = this.pos
62+
val newpd: this.type =
63+
if (posd.source == source && posd.pos == ownPos || ownPos.isSynthetic) this
64+
else cloneIn(posd.srcfile)
65+
newpd.setPos(posd.pos, posd.srcfile)
66+
newpd
67+
}
68+
69+
def withSourcePos(sourcePos: SourcePosition)(implicit ctx: Context): this.type = {
70+
val ownPos = this.pos
71+
val newpd: this.type =
72+
if (sourcePos.source == source && sourcePos.pos == ownPos || ownPos.isSynthetic) this
73+
else cloneIn(sourcePos.source.file)
74+
newpd.setPos(sourcePos.pos, sourcePos.source.file)
75+
newpd
4276
}
4377

4478
/** This item with a position that's the union of the given `pos` and the
@@ -50,7 +84,10 @@ abstract class Positioned extends Product {
5084
* any checks of consistency with - or updates of - other positions.
5185
* Called from Unpickler when entering positions.
5286
*/
53-
private[dotc] def setPosUnchecked(pos: Position): Unit = curPos = pos
87+
private[dotc] def setPosUnchecked(pos: Position, file: AbstractFile = this.srcfile): Unit = {
88+
if (file != this.srcfile) setId(TreeIds.nextIdFor(file))
89+
curPos = pos
90+
}
5491

5592
/** If any children of this node do not have positions,
5693
* fit their positions between the positions of the known subtrees
@@ -63,7 +100,7 @@ abstract class Positioned extends Product {
63100
* But since mutual tail recursion is not supported in Scala, we express it instead
64101
* as a while loop with a termination by return in the middle.
65102
*/
66-
private def setChildPositions(pos: Position): Unit = {
103+
private def setChildPositions(pos: Position, file: AbstractFile): Unit = {
67104
var n = productArity // subnodes are analyzed right to left
68105
var elems: List[Any] = Nil // children in lists still to be considered, from right to left
69106
var end = pos.end // the last defined offset, fill in positions up to this offset
@@ -75,10 +112,10 @@ abstract class Positioned extends Product {
75112
// synthetic. We can preserve this invariant by always setting a
76113
// zero-extent position for these trees here.
77114
if (!p.pos.exists || p.pos.isZeroExtent) {
78-
p.setPos(Position(start, start))
115+
p.setPos(Position(start, start), file)
79116
fillIn(ps1, start, end)
80117
} else {
81-
p.setPos(Position(start, end))
118+
p.setPos(Position(start, end), file)
82119
fillIn(ps1, end, end)
83120
}
84121
case nil =>
@@ -114,6 +151,31 @@ abstract class Positioned extends Product {
114151
}
115152
}
116153

154+
protected def cloneIn(file: AbstractFile): this.type = {
155+
val newpd: this.type = clone.asInstanceOf[this.type]
156+
newpd.setId(TreeIds.nextIdFor(file))
157+
newpd
158+
}
159+
160+
def initialFile(src: SourceInfo): AbstractFile = {
161+
def firstFile(x: Any): AbstractFile = x match {
162+
case x: Positioned if x.pos.exists =>
163+
x.srcfile
164+
case x1 :: xs1 =>
165+
val f = firstFile(x1)
166+
if (f != null) f else firstFile(xs1)
167+
case _ =>
168+
null
169+
}
170+
def firstElemFile(n: Int): AbstractFile =
171+
if (n == productArity) src.source.file
172+
else {
173+
val f = firstFile(productElement(n))
174+
if (f != null) f else firstElemFile(n + 1)
175+
}
176+
firstElemFile(0)
177+
}
178+
117179
/** The initial, synthetic position. This is usually the union of all positioned children's positions.
118180
*/
119181
def initialPos: Position = {
@@ -122,7 +184,7 @@ abstract class Positioned extends Product {
122184
while (n > 0) {
123185
n -= 1
124186
productElement(n) match {
125-
case p: Positioned => pos = pos union p.pos
187+
case p: Positioned if sameSource(p) => pos = pos union p.pos
126188
case m: untpd.Modifiers => pos = unionPos(unionPos(pos, m.mods), m.annotations)
127189
case xs: List[_] => pos = unionPos(pos, xs)
128190
case _ =>
@@ -132,12 +194,14 @@ abstract class Positioned extends Product {
132194
}
133195

134196
private def unionPos(pos: Position, xs: List[_]): Position = xs match {
135-
case (p: Positioned) :: xs1 => unionPos(pos union p.pos, xs1)
197+
case (p: Positioned) :: xs1 if sameSource(p) => unionPos(pos union p.pos, xs1)
136198
case (xs0: List[_]) :: xs1 => unionPos(unionPos(pos, xs0), xs1)
137199
case _ :: xs1 => unionPos(pos, xs1)
138200
case _ => pos
139201
}
140202

203+
private def sameSource(that: Positioned) = srcfile == that.srcfile
204+
141205
def contains(that: Positioned): Boolean = {
142206
def isParent(x: Any): Boolean = x match {
143207
case x: Positioned =>

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ 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 = {
20-
val file = src.source.file
19+
def nextId(implicit src: SourceInfo): Int = nextIdFor(src.source.file)
20+
21+
def nextIdFor(file: AbstractFile): Int = {
2122
var ctr = counters.get(file)
2223
if (ctr == null) {
2324
counters.putIfAbsent(file, new AtomicInteger)

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

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.AbstractFile
1515
import annotation.internal.sharable
1616
import annotation.unchecked.uncheckedVariance
1717
import annotation.transientParam
18+
import Decorators._
1819

1920
object Trees {
2021

@@ -55,42 +56,10 @@ object Trees {
5556
extends Positioned
5657
with Product
5758
with Attachment.Container
58-
with printing.Showable
59-
with Cloneable {
59+
with printing.Showable {
6060

6161
if (Stats.enabled) ntrees += 1
6262

63-
/** A unique identifier for this tree. Used for debugging, and potentially
64-
* tracking presentation compiler interactions.
65-
*/
66-
@sharable private var myUniqueId: Int = TreeIds.nextId
67-
68-
def uniqueId: Int = myUniqueId
69-
70-
def source(implicit ctx: Context): SourceFile = ctx.getSource(TreeIds.fileOfId(uniqueId))
71-
72-
def sourcePos(implicit ctx: Context): SourcePosition = source.atPos(pos)
73-
74-
def withPos(posd: Tree[_])(implicit ctx: Context): this.type = {
75-
val tree1 =
76-
if (posd.source == source && (posd.pos == pos || posd.pos.isSynthetic)) this
77-
else cloned(ctx.withSource(posd.source))
78-
tree1.setPos(sourcePos.pos)
79-
tree1.asInstanceOf[this.type]
80-
}
81-
82-
def withSource(source: SourceFile)(implicit ctx: Context): Tree[T] =
83-
if (source == this.source) this
84-
else cloned(ctx.withSource(source))
85-
86-
def withSourcePos(sourcePos: SourcePosition)(implicit ctx: Context): this.type = {
87-
val tree1 =
88-
if (sourcePos.source == source && (sourcePos.pos == pos || pos.isSynthetic)) this
89-
else cloned(ctx.withSource(sourcePos.source))
90-
tree1.setPos(sourcePos.pos)
91-
tree1.asInstanceOf[this.type]
92-
}
93-
9463
/** The type constructor at the root of the tree */
9564
type ThisTree[T >: Untyped] <: Tree[T]
9665

@@ -160,7 +129,7 @@ object Trees {
160129
val tree =
161130
(if (myTpe == null ||
162131
(myTpe.asInstanceOf[AnyRef] eq tpe.asInstanceOf[AnyRef])) this
163-
else clone).asInstanceOf[Tree[Type]]
132+
else cloneIn(srcfile)).asInstanceOf[Tree[Type]]
164133
tree overwriteType tpe
165134
tree.asInstanceOf[ThisTree[Type]]
166135
}
@@ -256,12 +225,6 @@ object Trees {
256225

257226
override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this)
258227
override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef]
259-
260-
override def cloned(implicit src: SourceInfo): Tree[T] = {
261-
val tree = clone.asInstanceOf[Tree[T]]
262-
tree.myUniqueId = TreeIds.nextId
263-
tree
264-
}
265228
}
266229

267230
class UnAssignedTypeException[T >: Untyped](tree: Tree[T]) extends RuntimeException {
@@ -357,7 +320,7 @@ object Trees {
357320
def rawComment: Option[Comment] = getAttachment(DocComment)
358321

359322
def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
360-
val tree = if (myMods == null || (myMods == mods)) this else clone.asInstanceOf[MemberDef[Untyped]]
323+
val tree = if (myMods == null || (myMods == mods)) this else cloneIn(srcfile)
361324
tree.setMods(mods)
362325
tree.asInstanceOf[ThisTree[Untyped]]
363326
}
@@ -809,9 +772,7 @@ object Trees {
809772
trait WithoutTypeOrPos[-T >: Untyped] extends Tree[T] {
810773
override def withTypeUnchecked(tpe: Type): ThisTree[Type] = this.asInstanceOf[ThisTree[Type]]
811774
override def pos: Position = NoPosition
812-
override def setPos(pos: Position): Unit = {}
813-
override def withSource(source: SourceFile)(implicit ctx: Context): Tree[T] = this
814-
override def withSourcePos(sourcePos: SourcePosition)(implicit ctx: Context): this.type = this
775+
override def setPos(pos: Position, file: AbstractFile): Unit = {}
815776
}
816777

817778
/** Temporary class that results from translation of ModuleDefs

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

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,36 +108,35 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
108108
*
109109
* For any query about semantic information, check `flags` instead.
110110
*/
111-
sealed abstract class Mod(val flags: FlagSet) extends Positioned {
112-
def cloned(implicit src: SourceInfo): Positioned = clone.asInstanceOf[Positioned]
113-
}
111+
sealed abstract class Mod(val flags: FlagSet)(implicit @transientParam src: SourceInfo)
112+
extends Positioned
114113

115114
object Mod {
116-
case class Private() extends Mod(Flags.Private)
115+
case class Private()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Private)
117116

118-
case class Protected() extends Mod(Flags.Protected)
117+
case class Protected()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Protected)
119118

120-
case class Var() extends Mod(Flags.Mutable)
119+
case class Var()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Mutable)
121120

122-
case class Implicit() extends Mod(Flags.ImplicitCommon)
121+
case class Implicit()(implicit @transientParam src: SourceInfo) extends Mod(Flags.ImplicitCommon)
123122

124-
case class Erased() extends Mod(Flags.Erased)
123+
case class Erased()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Erased)
125124

126-
case class Final() extends Mod(Flags.Final)
125+
case class Final()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Final)
127126

128-
case class Sealed() extends Mod(Flags.Sealed)
127+
case class Sealed()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Sealed)
129128

130-
case class Opaque() extends Mod(Flags.Opaque)
129+
case class Opaque()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Opaque)
131130

132-
case class Override() extends Mod(Flags.Override)
131+
case class Override()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Override)
133132

134-
case class Abstract() extends Mod(Flags.Abstract)
133+
case class Abstract()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Abstract)
135134

136-
case class Lazy() extends Mod(Flags.Lazy)
135+
case class Lazy()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Lazy)
137136

138-
case class Inline() extends Mod(Flags.Inline)
137+
case class Inline()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Inline)
139138

140-
case class Enum() extends Mod(Flags.Enum)
139+
case class Enum()(implicit @transientParam src: SourceInfo) extends Mod(Flags.Enum)
141140
}
142141

143142
/** Modifiers and annotations for definitions
@@ -151,7 +150,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
151150
flags: FlagSet = EmptyFlags,
152151
privateWithin: TypeName = tpnme.EMPTY,
153152
annotations: List[Tree] = Nil,
154-
mods: List[Mod] = Nil) extends Cloneable {
153+
mods: List[Mod] = Nil) {
155154

156155
def is(fs: FlagSet): Boolean = flags is fs
157156
def is(fc: FlagConjunction): Boolean = flags is fc
@@ -207,9 +206,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
207206

208207
def isEnumCase: Boolean = isEnum && is(Case)
209208
def isEnumClass: Boolean = isEnum && !is(Case)
210-
211-
override def cloned(implicit src: SourceInfo): Modifiers =
212-
clone.asInstanceOf[Modifiers]
213209
}
214210

215211
@sharable val EmptyModifiers: Modifiers = new Modifiers()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ object Contexts {
3939

4040
trait SourceInfo {
4141
def source: SourceFile
42+
def withSource(file: AbstractFile): SourceInfo
4243
}
4344

4445
private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer
2828
override def mkByNameClosure(arg: Tree, argType: Type)(implicit ctx: Context): Tree = {
2929
val meth = ctx.newSymbol(
3030
ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, argType))
31-
Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase))
31+
Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase)).withPos(arg)
3232
}
3333
}
3434

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ abstract class TransformByNameApply extends MiniPhase { thisPhase: DenotTransfor
4343
case formalExpr: ExprType =>
4444
var argType = arg.tpe.widenIfUnstable
4545
if (defn.isBottomType(argType)) argType = formal.widenExpr
46-
def wrap(arg: Tree) = ref(defn.cbnArg).appliedToType(argType).appliedTo(arg)
46+
def wrap(arg: Tree) =
47+
ref(defn.cbnArg).appliedToType(argType).appliedTo(arg).withPos(arg)
4748
arg match {
4849
case Apply(Select(qual, nme.apply), Nil)
4950
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ object Typer {
5757
/** Assert tree has a position, unless it is empty or a typed splice */
5858
def assertPositioned(tree: untpd.Tree)(implicit ctx: Context): Unit =
5959
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable)
60-
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
60+
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId} in ${tree.source}")
6161

6262
/** A context property that indicates the owner of any expressions to be typed in the context
6363
* if that owner is different from the context's owner. Typically, a context with a class

0 commit comments

Comments
 (0)