1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
package dotty.tools.dotc
package core
import Periods._
import Contexts._
import util.DotClass
import DenotTransformers._
import Denotations._
import config.Printers._
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
import dotty.tools.dotc.transform._
import Periods._
import typer.{FrontEnd, RefChecks}
import ast.tpd
trait Phases {
self: Context =>
import Phases._
def phase: Phase = base.phases(period.firstPhaseId)
def phasesStack: List[Phase] =
if ((this eq NoContext) || !phase.exists) Nil
else phase :: outersIterator.dropWhile(_.phase == phase).next.phasesStack
/** Execute `op` at given phase */
def atPhase[T](phase: Phase)(op: Context => T): T =
atPhase(phase.id)(op)
def atNextPhase[T](op: Context => T): T = atPhase(phase.next)(op)
def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T =
if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op)
def atPhaseNotLaterThanTyper[T](op: Context => T): T =
atPhaseNotLaterThan(base.typerPhase)(op)
def isAfterTyper: Boolean = base.isAfterTyper(phase)
}
object Phases {
trait PhasesBase {
this: ContextBase =>
// drop NoPhase at beginning
def allPhases = squashedPhases.tail
object NoPhase extends Phase {
override def exists = false
def phaseName = "<no phase>"
def run(implicit ctx: Context): Unit = unsupported("run")
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform")
}
object SomePhase extends Phase {
def phaseName = "<some phase>"
def run(implicit ctx: Context): Unit = unsupported("run")
}
/** A sentinel transformer object */
class TerminalPhase extends DenotTransformer {
def phaseName = "terminal"
def run(implicit ctx: Context): Unit = unsupported("run")
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation =
unsupported("transform")
override def lastPhaseId(implicit ctx: Context) = id
}
/** Squash TreeTransform's beloning to same sublist to a single TreeTransformer
* Each TreeTransform gets own period,
* whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms
*/
private def squashPhases(phasess: List[List[Phase]]): Array[Phase] = {
val squashedPhases = ListBuffer[Phase]()
var prevPhases: Set[Class[_ <: Phase]] = Set.empty
var i = 0
while (i < phasess.length) {
if (phasess(i).length > 1) {
val phasesInBlock: Set[String] = phasess(i).map(_.phaseName).toSet
for(phase<-phasess(i)) {
phase match {
case p: MiniPhase =>
val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases
assert(unmetRequirements.isEmpty,
s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer")
case _ =>
assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed")
}
}
val transforms = phasess(i).asInstanceOf[List[MiniPhase]].map(_.treeTransform)
val block = new TreeTransformer {
override def phaseName: String = transformations.map(_.phase.phaseName).mkString("TreeTransform:{", ", ", "}")
override def transformations: Array[TreeTransform] = transforms.toArray
}
squashedPhases += block
prevPhases ++= phasess(i).map(_.getClazz)
block.init(this, phasess(i).head.id, phasess(i).last.id)
} else {
squashedPhases += phasess(i).head
prevPhases += phasess(i).head.getClazz
}
i += 1
}
(NoPhase :: squashedPhases.toList ::: new TerminalPhase :: Nil).toArray
}
/** Use the following phases in the order they are given.
* The list should never contain NoPhase.
* if squashing is enabled, phases in same subgroup will be squashed to single phase.
*/
def usePhases(phasess: List[List[Phase]], squash: Boolean = true) = {
phases = (NoPhase :: phasess.flatten ::: new TerminalPhase :: Nil).toArray
var phasesAfter:Set[Class[_ <: Phase]] = Set.empty
nextDenotTransformerId = new Array[Int](phases.length)
denotTransformers = new Array[DenotTransformer](phases.length)
var i = 0
while (i < phases.length) {
phases(i).init(this, i)
val unmetPreceedeRequirements = phases(i).runsAfter -- phasesAfter
assert(unmetPreceedeRequirements.isEmpty,
s"phase ${phases(i)} has unmet requirement: ${unmetPreceedeRequirements.mkString(", ")} should precede this phase")
phasesAfter += phases(i).getClazz
i += 1
}
var lastTransformerId = i
while (i > 0) {
i -= 1
phases(i) match {
case transformer: DenotTransformer =>
lastTransformerId = i
denotTransformers(i) = transformer
case _ =>
}
nextDenotTransformerId(i) = lastTransformerId
}
if (squash) {
this.squashedPhases = squashPhases(phasess)
} else {
this.squashedPhases = this.phases
}
config.println(s"Phases = ${phases.deep}")
config.println(s"squashedPhases = ${squashedPhases.deep}")
config.println(s"nextDenotTransformerId = ${nextDenotTransformerId.deep}")
}
def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase)
/** A cache to compute the phase with given name, which
* stores the phase as soon as phaseNamed returns something
* different from NoPhase.
*/
private class PhaseCache(pclass: Class[_ <: Phase]) {
private var myPhase: Phase = NoPhase
def phase = {
if (myPhase eq NoPhase) myPhase = phaseOfClass(pclass)
myPhase
}
}
private val typerCache = new PhaseCache(classOf[FrontEnd])
private val refChecksCache = new PhaseCache(classOf[RefChecks])
private val erasureCache = new PhaseCache(classOf[Erasure])
private val patmatCache = new PhaseCache(classOf[PatternMatcher])
private val flattenCache = new PhaseCache(classOf[Flatten])
private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter])
private val gettersSettersCache = new PhaseCache(classOf[GettersSetters])
def typerPhase = typerCache.phase
def refchecksPhase = refChecksCache.phase
def erasurePhase = erasureCache.phase
def patmatPhase = patmatCache.phase
def flattenPhase = flattenCache.phase
def explicitOuterPhase = explicitOuterCache.phase
def gettersSettersPhase = gettersSettersCache.phase
def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id
}
trait Phase extends DotClass {
def phaseName: String
/** List of names of phases that should precede this phase */
def runsAfter: Set[Class[_ <: Phase]] = Set.empty
def run(implicit ctx: Context): Unit
def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] =
units.map { unit =>
val unitCtx = ctx.fresh.setPhase(this).setCompilationUnit(unit)
run(unitCtx)
unitCtx.compilationUnit
}
def description: String = phaseName
/** Output should be checkable by TreeChecker */
def isCheckable: Boolean = true
/** Check what the phase achieves, to be called at any point after it is finished.
*/
def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = ()
/** If set, allow missing or superfluous arguments in applications
* and type applications.
*/
def relaxedTyping: Boolean = false
def exists: Boolean = true
private var myPeriod: Period = Periods.InvalidPeriod
private var myBase: ContextBase = null
private var myErasedTypes = false
private var myFlatClasses = false
private var myRefChecked = false
private var mySymbolicRefs = false
/** The sequence position of this phase in the given context where 0
* is reserved for NoPhase and the first real phase is at position 1.
* -1 if the phase is not installed in the context.
*/
def id = myPeriod.firstPhaseId
def period = myPeriod
def start = myPeriod.firstPhaseId
def end = myPeriod.lastPhaseId
final def erasedTypes = myErasedTypes // Phase is after erasure
final def flatClasses = myFlatClasses // Phase is after flatten
final def refChecked = myRefChecked // Phase is after RefChecks
final def symbolicRefs = mySymbolicRefs // Phase is after ResolveSuper, newly generated TermRefs should be symbolic
protected[Phases] def init(base: ContextBase, start: Int, end:Int): Unit = {
if (start >= FirstPhaseId)
assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused")
myBase = base
myPeriod = Period(NoRunId, start, end)
myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes
myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
mySymbolicRefs = prev.getClass == classOf[ResolveSuper] || prev.symbolicRefs
}
protected[Phases] def init(base: ContextBase, id: Int): Unit = init(base, id, id)
final def <=(that: Phase) =
exists && id <= that.id
final def prev: Phase =
if (id > FirstPhaseId) myBase.phases(start - 1) else myBase.NoPhase
final def next: Phase =
if (hasNext) myBase.phases(end + 1) else myBase.NoPhase
final def hasNext = start >= FirstPhaseId && end + 1 < myBase.phases.length
final def iterator =
Iterator.iterate(this)(_.next) takeWhile (_.hasNext)
override def toString = phaseName
}
/** Dotty deviation: getClass yields Class[_], instead of [Class <: <type of receiver>].
* We can get back the old behavior using this decorator. We should also use the same
* trick for standard getClass.
*/
private implicit class getClassDeco[T](val x: T) extends AnyVal {
def getClazz: Class[_ <: T] = x.getClass.asInstanceOf[Class[_ <: T]]
}
}
|