@@ -72,9 +72,7 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase =>
7272 override def transformTemplate (impl : Template )(using Context ): Tree = {
7373 val cls = ctx.owner.asClass
7474 val isTrait = cls.is(Trait )
75- if (needsOuterIfReferenced(cls) &&
76- ! needsOuterAlways(cls) &&
77- impl.existsSubTree(referencesOuter(cls, _)))
75+ if needsOuterIfReferenced(cls) && ! needsOuterAlways(cls) && referencesOuter(cls, impl) then
7876 ensureOuterAccessors(cls)
7977
8078 val clsHasOuter = hasOuter(cls)
@@ -255,55 +253,83 @@ object ExplicitOuter {
255253
256254 /** Tree references an outer class of `cls` which is not a static owner.
257255 */
258- def referencesOuter (cls : Symbol , tree : Tree )(using Context ): Boolean = {
259- def isOuterSym (sym : Symbol ) =
260- ! sym.isStaticOwner && cls.isProperlyContainedIn(sym)
261- def isOuterRef (ref : Type ): Boolean = ref match {
262- case ref : ThisType =>
263- isOuterSym(ref.cls)
264- case ref : TermRef =>
265- if (ref.prefix ne NoPrefix )
266- ! ref.symbol.isStatic && isOuterRef(ref.prefix)
267- else (
268- ref.symbol.isOneOf(HoistableFlags ) &&
269- // ref.symbol will be placed in enclosing class scope by LambdaLift, so it might need
270- // an outer path then.
271- isOuterSym(ref.symbol.owner.enclosingClass)
272- ||
273- // If not hoistable, ref.symbol will get a proxy in immediately enclosing class. If this properly
274- // contains the current class, it needs an outer path.
275- // If the symbol is hoistable, it might have free variables for which the same
276- // reasoning applies. See pos/i1664.scala
277- ctx.owner.enclosingClass.owner.enclosingClass.isContainedIn(ref.symbol.owner)
278- )
279- case _ => false
280- }
281- def hasOuterPrefix (tp : Type ): Boolean = tp.stripped match {
282- case AppliedType (tycon, _) => hasOuterPrefix(tycon)
283- case TypeRef (prefix, _) => isOuterRef(prefix)
284- case _ => false
285- }
286- def containsOuterRefs (tp : Type ): Boolean = tp match
287- case tp : SingletonType => isOuterRef(tp)
288- case tp : AndOrType => containsOuterRefs(tp.tp1) || containsOuterRefs(tp.tp2)
289- case _ => false
290- tree match {
291- case _ : This | _ : Ident => isOuterRef(tree.tpe)
292- case nw : New =>
293- val newCls = nw.tpe.classSymbol
294- isOuterSym(newCls.owner.enclosingClass) ||
295- hasOuterPrefix(nw.tpe) ||
296- newCls.owner.isTerm && cls.isProperlyContainedIn(newCls)
297- // newCls might get proxies for free variables. If current class is
298- // properly contained in newCls, it needs an outer path to newCls access the
299- // proxies and forward them to the new instance.
300- case app : TypeApply if app.symbol.isTypeTest =>
301- // Type tests of singletons translate to `eq` tests with references, which might require outer pointers
302- containsOuterRefs(app.args.head.tpe)
303- case _ =>
304- false
305- }
306- }
256+ def referencesOuter (cls : Symbol , tree : Tree )(using Context ): Boolean =
257+
258+
259+ val test = new TreeAccumulator [Boolean ]:
260+ private var inInline = false
261+
262+ def isOuterSym (sym : Symbol ) =
263+ ! sym.isStaticOwner && cls.isProperlyContainedIn(sym)
264+
265+ def isOuterRef (ref : Type ): Boolean = ref match
266+ case ref : ThisType =>
267+ isOuterSym(ref.cls)
268+ case ref : TermRef =>
269+ if (ref.prefix ne NoPrefix )
270+ ! ref.symbol.isStatic && isOuterRef(ref.prefix)
271+ else (
272+ ref.symbol.isOneOf(HoistableFlags ) &&
273+ // ref.symbol will be placed in enclosing class scope by LambdaLift, so it might need
274+ // an outer path then.
275+ isOuterSym(ref.symbol.owner.enclosingClass)
276+ ||
277+ // If not hoistable, ref.symbol will get a proxy in immediately enclosing class. If this properly
278+ // contains the current class, it needs an outer path.
279+ // If the symbol is hoistable, it might have free variables for which the same
280+ // reasoning applies. See pos/i1664.scala
281+ ctx.owner.enclosingClass.owner.enclosingClass.isContainedIn(ref.symbol.owner)
282+ )
283+ case _ => false
284+
285+ def hasOuterPrefix (tp : Type ): Boolean = tp.stripped match
286+ case AppliedType (tycon, _) => hasOuterPrefix(tycon)
287+ case TypeRef (prefix, _) => isOuterRef(prefix)
288+ case _ => false
289+
290+ def containsOuterRefsAtTopLevel (tp : Type ): Boolean = tp match
291+ case tp : SingletonType => isOuterRef(tp)
292+ case tp : AndOrType => containsOuterRefsAtTopLevel(tp.tp1) || containsOuterRefsAtTopLevel(tp.tp2)
293+ case _ => false
294+
295+ def containsOuterRefsAnywhere (tp : Type ): Boolean =
296+ tp.existsPart({
297+ case t : SingletonType => isOuterRef(t)
298+ case _ => false
299+ }, StopAt .Static )
300+
301+ def containsOuterRefs (t : Tree ): Boolean = t match
302+ case _ : This | _ : Ident => isOuterRef(t.tpe)
303+ case nw : New =>
304+ val newCls = nw.tpe.classSymbol
305+ isOuterSym(newCls.owner.enclosingClass) ||
306+ hasOuterPrefix(nw.tpe) ||
307+ newCls.owner.isTerm && cls.isProperlyContainedIn(newCls)
308+ // newCls might get proxies for free variables. If current class is
309+ // properly contained in newCls, it needs an outer path to newCls access the
310+ // proxies and forward them to the new instance.
311+ case app : TypeApply if app.symbol.isTypeTest =>
312+ // Type tests of singletons translate to `eq` tests with references, which might require outer pointers
313+ containsOuterRefsAtTopLevel(app.args.head.tpe)
314+ case t : TypeTree if inInline =>
315+ // Expansions of inline methods must be able to address outer types
316+ containsOuterRefsAnywhere(t.tpe)
317+ case _ =>
318+ false
319+
320+ def apply (x : Boolean , t : Tree )(using Context ) =
321+ if x || containsOuterRefs(t) then true
322+ else t match
323+ case t : DefDef if t.symbol.isInlineMethod =>
324+ val saved = inInline
325+ inInline = true
326+ try foldOver(x, t)
327+ finally inInline = saved
328+ case _ =>
329+ foldOver(x, t)
330+
331+ test(false , tree)
332+ end referencesOuter
307333
308334 private final val HoistableFlags = Method | Lazy | Module
309335
0 commit comments