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
|
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc
package transform
import symtab._
import Flags._
import scala.collection.mutable.ListBuffer
abstract class Flatten extends InfoTransform {
import global._
import treeInfo.isQualifierSafeToElide
/** the following two members override abstract members in Transform */
val phaseName: String = "flatten"
/** Updates the owning scope with the given symbol, unlinking any others.
*/
private def replaceSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten {
removeSymbolInCurrentScope(sym)
sym.owner.info.decls enter sym
}
private def removeSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten {
val scope = sym.owner.info.decls
val old = (scope lookupUnshadowedEntries sym.name).toList
old foreach (scope unlink _)
def old_s = old map (_.sym) mkString ", "
if (old.nonEmpty) debuglog(s"In scope of ${sym.owner}, unlinked $old_s")
}
private def liftClass(sym: Symbol) {
if (!sym.isLifted) {
sym setFlag LIFTED
debuglog("re-enter " + sym.fullLocationString)
replaceSymbolInCurrentScope(sym)
}
}
private def liftSymbol(sym: Symbol) {
liftClass(sym)
if (sym.needsImplClass)
liftClass(erasure implClass sym)
}
// This is a short-term measure partially working around objects being
// lifted out of parameterized classes, leaving them referencing
// invisible type parameters.
private def isFlattenablePrefix(pre: Type) = {
val clazz = pre.typeSymbol
clazz.isClass && !clazz.isPackageClass && {
// Cannot flatten here: class A[T] { object B }
// was "at erasurePhase.prev"
enteringErasure(clazz.typeParams.isEmpty)
}
}
private val flattened = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) if isFlattenablePrefix(pre) =>
assert(args.isEmpty && sym.enclosingTopLevelClass != NoSymbol, sym.ownerChain)
typeRef(sym.enclosingTopLevelClass.owner.thisType, sym, Nil)
case ClassInfoType(parents, decls, clazz) =>
var parents1 = parents
val decls1 = scopeTransform(clazz) {
val decls1 = newScope
if (clazz.isPackageClass) {
exitingFlatten { decls foreach (decls1 enter _) }
}
else {
val oldowner = clazz.owner
exitingFlatten { oldowner.info }
parents1 = parents mapConserve (this)
for (sym <- decls) {
if (sym.isTerm && !sym.isStaticModule) {
decls1 enter sym
if (sym.isModule) {
// In theory, we could assert(sym.isMethod), because nested, non-static modules are
// transformed to methods (lateMETHOD flag added in RefChecks). But this requires
// forcing sym.info (see comment on isModuleNotMethod), which forces stub symbols
// too eagerly (SI-8907).
// Note that module classes are not entered into the 'decls' of the ClassInfoType
// of the outer class, only the module symbols are. So the current loop does
// not visit module classes. Therefore we set the LIFTED flag here for module
// classes.
// TODO: should we also set the LIFTED flag for static, nested module classes?
// currently they don't get the flag, even though they are lifted to the package
sym.moduleClass setFlag LIFTED
}
} else if (sym.isClass)
liftSymbol(sym)
}
}
decls1
}
ClassInfoType(parents1, decls1, clazz)
case MethodType(params, restp) =>
val restp1 = apply(restp)
if (restp1 eq restp) tp else copyMethodType(tp, params, restp1)
case PolyType(tparams, restp) =>
val restp1 = apply(restp)
if (restp1 eq restp) tp else PolyType(tparams, restp1)
case _ =>
mapOver(tp)
}
}
def transformInfo(sym: Symbol, tp: Type): Type = flattened(tp)
protected def newTransformer(unit: CompilationUnit): Transformer = new Flattener
class Flattener extends Transformer {
/** Buffers for lifted out classes */
private val liftedDefs = perRunCaches.newMap[Symbol, ListBuffer[Tree]]()
override def transform(tree: Tree): Tree = postTransform {
tree match {
case PackageDef(_, _) =>
liftedDefs(tree.symbol.moduleClass) = new ListBuffer
super.transform(tree)
case Template(_, _, _) if tree.symbol.isDefinedInPackage =>
liftedDefs(tree.symbol.owner) = new ListBuffer
super.transform(tree)
case ClassDef(_, _, _, _) if tree.symbol.isNestedClass =>
// SI-5508 Ordering important. In `object O { trait A { trait B } }`, we want `B` to appear after `A` in
// the sequence of lifted trees in the enclosing package. Why does this matter? Currently, mixin
// needs to transform `A` first to a chance to create accessors for private[this] trait fields
// *before* it transforms inner classes that refer to them. This also fixes SI-6231.
//
// Alternative solutions
// - create the private[this] accessors eagerly in Namer (but would this cover private[this] fields
// added later phases in compilation?)
// - move the accessor creation to the Mixin info transformer
val liftedBuffer = liftedDefs(tree.symbol.enclosingTopLevelClass.owner)
val index = liftedBuffer.length
liftedBuffer.insert(index, super.transform(tree))
if (tree.symbol.sourceModule.isStaticModule)
removeSymbolInCurrentScope(tree.symbol.sourceModule)
EmptyTree
case _ =>
super.transform(tree)
}
}
private def postTransform(tree: Tree): Tree = {
val sym = tree.symbol
val tree1 = tree match {
case Select(qual, name) if sym.isStaticModule && !sym.isTopLevel =>
exitingFlatten {
atPos(tree.pos) {
val ref = gen.mkAttributedRef(sym)
if (isQualifierSafeToElide(qual)) ref
else Block(List(qual), ref).setType(tree.tpe) // need to execute the qualifier but refer directly to the lifted module.
}
}
case _ =>
tree
}
tree1 setType flattened(tree1.tpe)
}
/** Transform statements and add lifted definitions to them. */
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
val stats1 = super.transformStats(stats, exprOwner)
if (currentOwner.isPackageClass) {
val lifted = liftedDefs.remove(currentOwner).toList.flatten
stats1 ::: lifted
}
else stats1
}
}
}
|