@@ -18,9 +18,8 @@ import collection.mutable
18
18
19
19
/** This phase adds outer accessors to classes and traits that need them.
20
20
* Compared to Scala 2.x, it tries to minimize the set of classes
21
- * that take outer accessors and also tries to minimize the number
22
- * of objects referred to by outer accessors. This helps prevent space
23
- * leaks.
21
+ * that take outer accessors by scanning class implementations for
22
+ * outer references.
24
23
*
25
24
* The following things are delayed until erasure and are performed
26
25
* by class OuterOps:
@@ -43,7 +42,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
43
42
override def transformInfo (tp : Type , sym : Symbol )(implicit ctx : Context ) = tp match {
44
43
case tp @ ClassInfo (_, cls, _, decls, _) if needsOuterAlways(cls) =>
45
44
val newDecls = decls.cloneScope
46
- newExplicitOuter (cls).foreach(newDecls.enter)
45
+ newOuterAccessors (cls).foreach(newDecls.enter)
47
46
tp.derivedClassInfo(decls = newDecls)
48
47
case _ =>
49
48
tp
@@ -67,12 +66,12 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
67
66
newOuterSym(cls, cls, nme.OUTER , Private | ParamAccessor )
68
67
69
68
/** The outer accessor and potentially outer param accessor needed for class `cls` */
70
- private def newExplicitOuter (cls : ClassSymbol )(implicit ctx : Context ) =
69
+ private def newOuterAccessors (cls : ClassSymbol )(implicit ctx : Context ) =
71
70
newOuterAccessor(cls, cls) :: (if (cls is Trait ) Nil else newOuterParamAccessor(cls) :: Nil )
72
71
73
72
/** First, add outer accessors if a class does not have them yet and it references an outer this.
74
73
* If the class has outer accessors, implement them.
75
- * Furthermore, if a parent trait might have outer accessors (decided by needsOuterIfReferenced) ,
74
+ * Furthermore, if a parent trait might have an outer accessor ,
76
75
* provide an implementation for the outer accessor by computing the parent's
77
76
* outer from the parent type prefix. If the trait ends up not having an outer accessor
78
77
* after all, the implementation is redundant, but does not harm.
@@ -84,8 +83,10 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
84
83
override def transformTemplate (impl : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = {
85
84
val cls = ctx.owner.asClass
86
85
val isTrait = cls.is(Trait )
87
- if (needsOuterIfReferenced(cls) && ! needsOuterAlways(cls) && referencesOuter(cls, impl))
88
- newExplicitOuter(cls).foreach(_.enteredAfter(thisTransformer))
86
+ if (needsOuterIfReferenced(cls) &&
87
+ ! needsOuterAlways(cls) &&
88
+ existsSubTreeOf(impl)(referencesOuter(cls, _)))
89
+ newOuterAccessors(cls).foreach(_.enteredAfter(thisTransformer))
89
90
if (hasOuter(cls)) {
90
91
val newDefs = new mutable.ListBuffer [Tree ]
91
92
if (isTrait)
@@ -150,7 +151,15 @@ object ExplicitOuter {
150
151
private def outerParamAccessor (cls : ClassSymbol )(implicit ctx : Context ): TermSymbol =
151
152
cls.info.decl(nme.OUTER ).symbol.asTerm
152
153
153
- /** The outer access of class `cls` */
154
+ /** The outer accessor of class `cls`. To find it is a bit tricky. The
155
+ * class might have been moved with new owners between ExplicitOuter and Erasure,
156
+ * where the method is also called. For instance, it might have been part
157
+ * of a by-name argument, and therefore be moved under a closure method
158
+ * by ElimByName. In that case looking up the method again at Erasure with the
159
+ * fully qualified name `outerAccName` will fail, because the `outerAccName`'s
160
+ * result is phase dependent. In that case we use a backup strategy where we search all
161
+ * definitions in the class to find the one with the OuterAccessor flag.
162
+ */
154
163
private def outerAccessor (cls : ClassSymbol )(implicit ctx : Context ): Symbol =
155
164
cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor ).symbol orElse
156
165
cls.info.decls.find(_ is OuterAccessor ).getOrElse(NoSymbol )
@@ -159,17 +168,28 @@ object ExplicitOuter {
159
168
private def hasOuter (cls : ClassSymbol )(implicit ctx : Context ): Boolean =
160
169
needsOuterIfReferenced(cls) && outerAccessor(cls).exists
161
170
162
- /** Template `impl` of class `cls` references an outer this which goes to
163
- * a class that is not a static owner.
171
+ /** Tree references a an outer class of `cls` which is not a static owner.
164
172
*/
165
- private def referencesOuter (cls : ClassSymbol , impl : Template )(implicit ctx : Context ): Boolean =
166
- existsSubTreeOf(impl) {
173
+ def referencesOuter (cls : Symbol , tree : Tree )(implicit ctx : Context ): Boolean = {
174
+ def isOuter (sym : Symbol ) =
175
+ sym != cls && ! sym.isStaticOwner && cls.isContainedIn(sym)
176
+ tree match {
167
177
case thisTree @ This (_) =>
168
- val thisCls = thisTree.symbol
169
- thisCls != cls && ! thisCls.isStaticOwner && cls.isContainedIn(thisCls)
178
+ isOuter(thisTree.symbol)
179
+ case id : Ident =>
180
+ id.tpe match {
181
+ case ref @ TermRef (NoPrefix , _) =>
182
+ ref.symbol.is(Method ) && isOuter(id.symbol.owner.enclosingClass)
183
+ // methods will be placed in enclosing class scope by LambdaLift, so they will get
184
+ // an outer path then.
185
+ case _ => false
186
+ }
187
+ case nw : New =>
188
+ isOuter(nw.tpe.classSymbol.owner.enclosingClass)
170
189
case _ =>
171
- false
190
+ false
172
191
}
192
+ }
173
193
174
194
/** The outer prefix implied by type `tpe` */
175
195
private def outerPrefix (tpe : Type )(implicit ctx : Context ): Type = tpe match {
@@ -222,13 +242,16 @@ object ExplicitOuter {
222
242
if (fun.symbol.isConstructor) {
223
243
val cls = fun.symbol.owner.asClass
224
244
def outerArg (receiver : Tree ): Tree = receiver match {
225
- case New (tpt) => TypeTree (outerPrefix(tpt.tpe)).withPos(tpt.pos)
226
- case This (_) => ref(outerParamAccessor(cls))
227
- case TypeApply (Select (r, nme.asInstanceOf_), args) => outerArg(r) // cast was inserted, skip
245
+ case New (tpt) =>
246
+ singleton(outerPrefix(tpt.tpe))
247
+ case This (_) =>
248
+ ref(outerParamAccessor(cls)) // will be rewried to outer argument of secondary constructor in phase Constructors
249
+ case TypeApply (Select (r, nme.asInstanceOf_), args) =>
250
+ outerArg(r) // cast was inserted, skip
228
251
}
229
252
if (hasOuter(cls))
230
253
methPart(fun) match {
231
- case Select (receiver, _) => outerArg(receiver) :: Nil
254
+ case Select (receiver, _) => outerArg(receiver).withPos(fun.pos) :: Nil
232
255
}
233
256
else Nil
234
257
} else Nil
0 commit comments