-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathFirstTransform.scala
223 lines (198 loc) · 8.6 KB
/
FirstTransform.scala
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
package dotty.tools.dotc
package transform
import core.*
import Names.*
import dotty.tools.dotc.transform.MegaPhase.*
import ast.untpd
import Flags.*
import Types.*
import Constants.Constant
import Contexts.*
import Symbols.*
import Decorators.*
import scala.collection.mutable
import DenotTransformers.*
import NameOps.*
import SymDenotations.SymDenotation
import NameKinds.OuterSelectName
import StdNames.*
import config.Feature
import inlines.Inlines.inInlineMethod
object FirstTransform {
val name: String = "firstTransform"
val description: String = "some transformations to put trees into a canonical form"
}
/** The first tree transform
* - eliminates some kinds of trees: Imports other than language imports,
* Exports, NamedArgs, type trees other than TypeTree
* - stubs out native methods
* - eliminates self tree in Template and self symbol in ClassInfo
* - collapses all type trees to trees of class TypeTree
* - converts idempotent expressions with constant types
* - drops branches of ifs using the rules
* if (true) A else B ==> A
* if (false) A else B ==> B
*/
class FirstTransform extends MiniPhase with SymTransformer { thisPhase =>
import ast.tpd.*
override def phaseName: String = FirstTransform.name
override def description: String = FirstTransform.description
/** eliminate self symbol in ClassInfo, reset Deferred for @native methods */
override def transformSym(sym: SymDenotation)(using Context): SymDenotation =
if sym.isClass then
sym.info match
case tp @ ClassInfo(_, _, _, _, self: Symbol) =>
val info1 = tp.derivedClassInfo(selfInfo = self.info)
sym.copySymDenotation(info = info1).copyCaches(sym, ctx.phase.next)
case _ =>
sym
else if sym.isAllOf(DeferredMethod) && sym.hasAnnotation(defn.NativeAnnot) then
sym.copySymDenotation(initFlags = sym.flags &~ Deferred)
else
sym
override def checkPostCondition(tree: Tree)(using Context): Unit =
tree match {
case Select(qual, name) if !name.is(OuterSelectName) && tree.symbol.exists =>
val qualTpe = qual.tpe
assert(
qualTpe.isErasedValueType || qualTpe.derivesFrom(tree.symbol.owner) ||
tree.symbol.is(JavaStatic) && qualTpe.derivesFrom(tree.symbol.enclosingClass),
i"non member selection of ${tree.symbol.showLocated} from ${qualTpe} in $tree")
case _: TypeTree =>
case _: Export | _: NamedArg | _: TypTree =>
assert(false, i"illegal tree: $tree")
case _ =>
}
/** Reorder statements so that module classes always come after their companion classes */
private def reorderAndComplete(stats: List[Tree])(using Context): List[Tree] = {
val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]()
/* Returns the result of reordering stats and prepending revPrefix in reverse order to it.
* The result of reorder is equivalent to reorder(stats, revPrefix) = revPrefix.reverse ::: reorder(stats, Nil).
* This implementation is tail recursive as long as the element is not a module TypeDef.
*/
def reorder(stats: List[Tree], revPrefix: List[Tree]): List[Tree] = stats match {
case (stat: TypeDef) :: stats1 if stat.symbol.isClass =>
if (stat.symbol.is(Flags.Module)) {
def pushOnTop(xs: List[Tree], ys: List[Tree]): List[Tree] =
xs.foldLeft(ys)((ys, x) => x :: ys)
moduleClassDefs += (stat.name -> stat)
singleClassDefs -= stat.name.stripModuleClassSuffix
val stats1r = reorder(stats1, Nil)
pushOnTop(revPrefix, if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r)
}
else
reorder(
stats1,
moduleClassDefs remove stat.name.moduleClassName match {
case Some(mcdef) =>
mcdef :: stat :: revPrefix
case None =>
singleClassDefs += (stat.name -> stat)
stat :: revPrefix
}
)
case stat :: stats1 => reorder(stats1, stat :: revPrefix)
case Nil => revPrefix.reverse
}
reorder(stats, Nil)
}
/** Eliminate self in Template
* Under captureChecking, we keep the self type `S` around in a type definition
*
* private[this] type $this = S
*
* This is so that the type can be checked for well-formedness in the CaptureCheck phase.
*/
override def transformTemplate(impl: Template)(using Context): Tree =
impl.self match
case self: ValDef if !self.tpt.isEmpty && Feature.ccEnabled =>
val tsym = newSymbol(ctx.owner, tpnme.SELF, PrivateLocal, TypeAlias(self.tpt.tpe))
val tdef = untpd.cpy.TypeDef(self)(tpnme.SELF, self.tpt).withType(tsym.typeRef)
cpy.Template(impl)(self = EmptyValDef, body = tdef :: impl.body)
case _ =>
cpy.Template(impl)(self = EmptyValDef)
override def transformDefDef(ddef: DefDef)(using Context): Tree =
val meth = ddef.symbol.asTerm
if meth.hasAnnotation(defn.NativeAnnot) then
DefDef(meth, _ =>
ref(defn.Sys_error.termRef).withSpan(ddef.span)
.appliedTo(Literal(Constant(s"native method stub"))))
else ddef
override def transformStats(trees: List[Tree])(using Context): List[Tree] =
ast.Trees.flatten(atPhase(thisPhase.next)(reorderAndComplete(trees)))
private object collectBinders extends TreeAccumulator[List[Ident]] {
def apply(annots: List[Ident], t: Tree)(using Context): List[Ident] = t match {
case t @ Bind(_, body) =>
val annot = untpd.Ident(tpnme.BOUNDTYPE_ANNOT).withType(t.symbol.typeRef)
apply(annot :: annots, body)
case _ =>
foldOver(annots, t)
}
}
/** Replace type tree `t` of type `T` with `TypeTree(T)`, but record all
* nested Bind nodes in annotations. These are interpreted in TreeTypeMaps
* so that bound symbols can be properly copied.
*/
private def toTypeTree(tree: Tree)(using Context) = {
val binders = collectBinders.apply(Nil, tree)
val result: Tree = TypeTree(tree.tpe).withSpan(tree.span)
binders.foldLeft(result)(Annotated(_, _))
}
override def transformOther(tree: Tree)(using Context): Tree = tree match {
case tree: Export => EmptyTree
case tree: NamedArg => transformAllDeep(tree.arg)
case tree => if (tree.isType) toTypeTree(tree) else tree
}
override def transformIdent(tree: Ident)(using Context): Tree =
if (tree.isType) {
toTypeTree(tree)
} else if (tree.name != nme.WILDCARD) {
// We constant-fold all idents except wildcards.
// AFAIK, constant-foldable wildcard idents can only occur in patterns, for instance as `case _: "a"`.
// Constant-folding that would result in `case "a": "a"`, which changes the meaning of the pattern.
// Note that we _do_ want to constant-fold idents in patterns that _aren't_ wildcards -
// for example, @switch annotation needs to see inlined literals and not indirect references.
constToLiteral(tree)
} else tree
override def transformSelect(tree: Select)(using Context): Tree =
if (tree.isType) toTypeTree(tree) else constToLiteral(tree)
override def transformTypeApply(tree: TypeApply)(using Context): Tree =
constToLiteral(tree)
override def transformApply(tree: Apply)(using Context): Tree =
constToLiteral(foldCondition(tree))
override def transformTyped(tree: Typed)(using Context): Tree =
// Singleton type cases (such as `case _: "a"`) are constant-foldable.
// We avoid constant-folding those as doing so would change the meaning of the pattern (see transformIdent).
if (!ctx.mode.is(Mode.Pattern)) constToLiteral(tree) else tree
override def transformBlock(tree: Block)(using Context): Tree =
constToLiteral(tree)
override def transformIf(tree: If)(using Context): Tree =
tree.cond.tpe match {
case ConstantType(Constant(c: Boolean)) if isPureExpr(tree.cond) =>
if (c) tree.thenp else tree.elsep
case _ => tree
}
/** Perform one of the following simplification if applicable:
*
* true && y ==> y
* false && y ==> false
* true || y ==> true
* false || y ==> y
*/
private def foldCondition(tree: Apply)(using Context) = tree.fun match {
case Select(x @ Literal(Constant(c: Boolean)), op) =>
tree.args match {
case y :: Nil if y.tpe.widen.isRef(defn.BooleanClass) =>
op match {
case nme.ZAND => if (c) y else x
case nme.ZOR => if (c) x else y
case _ => tree
}
case _ => tree
}
case _ => tree
}
// invariants: all modules have companion objects
// all types are TypeTrees
// all this types are explicit
}