aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2016-03-07 16:50:12 +0100
committerodersky <odersky@gmail.com>2016-03-07 16:50:12 +0100
commit8b4f73fb73a2f57a0b3e6088443797e40f1d1720 (patch)
tree6e44261a51af83dff8bdaa43e330ce3af1be1153 /src/dotty/tools/dotc
parenta50926701ef5171779aa025d2d307751d166cabe (diff)
parentcf2fed8138cb399beb7d1249227107b943fe3905 (diff)
downloaddotty-8b4f73fb73a2f57a0b3e6088443797e40f1d1720.tar.gz
dotty-8b4f73fb73a2f57a0b3e6088443797e40f1d1720.tar.bz2
dotty-8b4f73fb73a2f57a0b3e6088443797e40f1d1720.zip
Merge pull request #1150 from dotty-staging/change-lambdalift-fasttrack
Change lambdalift - fasttrack
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala13
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala210
-rw-r--r--src/dotty/tools/dotc/transform/SymUtils.scala14
4 files changed, 156 insertions, 83 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index be4477ee2..aee9b5b15 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -75,7 +75,7 @@ class Compiler {
new Constructors, // constructors changes decls in transformTemplate, no InfoTransformers should be added after it
new FunctionalInterfaces,
new GetClass), // getClass transformation should be applied to specialized methods
- List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
+ List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
new ElimStaticThis,
new Flatten,
// new DropEmptyCompanions,
diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index 024247734..4cf076c45 100644
--- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -209,7 +209,7 @@ object ExplicitOuter {
private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
!cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists
- /** Tree references a an outer class of `cls` which is not a static owner.
+ /** Tree references an outer class of `cls` which is not a static owner.
*/
def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = {
def isOuter(sym: Symbol) =
@@ -231,7 +231,12 @@ object ExplicitOuter {
case _ => false
}
case nw: New =>
- isOuter(nw.tpe.classSymbol.owner.enclosingClass)
+ val newCls = nw.tpe.classSymbol
+ isOuter(newCls.owner.enclosingClass) ||
+ newCls.owner.isTerm && cls.isProperlyContainedIn(newCls)
+ // newCls might get proxies for free variables. If current class is
+ // properly contained in newCls, it needs an outer path to newCls access the
+ // proxies and forward them to the new instance.
case _ =>
false
}
@@ -308,7 +313,7 @@ object ExplicitOuter {
/** The path of outer accessors that references `toCls.this` starting from
* the context owner's this node.
*/
- def path(toCls: Symbol): Tree = try {
+ def path(toCls: Symbol, start: Tree = This(ctx.owner.enclosingClass.asClass)): Tree = try {
def loop(tree: Tree): Tree = {
val treeCls = tree.tpe.widen.classSymbol
val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore
@@ -317,7 +322,7 @@ object ExplicitOuter {
else loop(tree.select(outerAccessor(treeCls.asClass)(outerAccessorCtx)).ensureApplied)
}
ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}")
- loop(This(ctx.owner.enclosingClass.asClass))
+ loop(start)
} catch {
case ex: ClassCastException =>
throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls")
diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala
index 19ad06085..3ef684e55 100644
--- a/src/dotty/tools/dotc/transform/LambdaLift.scala
+++ b/src/dotty/tools/dotc/transform/LambdaLift.scala
@@ -26,6 +26,37 @@ object LambdaLift {
private class NoPath extends Exception
}
+/** This phase performs the necessary rewritings to eliminate classes and methods
+ * nested in other methods. In detail:
+ * 1. It adds all free variables of local functions as additional parameters (proxies).
+ * 2. It rebinds references to free variables to the corresponding proxies,
+ * 3. It lifts all local functions and classes out as far as possible, but at least
+ * to the enclosing class.
+ * 4. It stores free variables of non-trait classes as additional fields of the class.
+ * The fields serve as proxies for methods in the class, which avoids the need
+ * of passing additional parameters to these methods.
+ *
+ * A particularly tricky case are local traits. These cannot store free variables
+ * as field proxies, because LambdaLift runs after Mixin, so the fields cannot be
+ * expanded anymore. Instead, methods of local traits get free variables of
+ * the trait as additional proxy parameters. The difference between local classes
+ * and local traits is illustrated by the two rewritings below.
+ *
+ * def f(x: Int) = { def f(x: Int) = new C(x).f2
+ * class C { ==> class C(x$1: Int) {
+ * def f2 = x def f2 = x$1
+ * } }
+ * new C().f2
+ * }
+ *
+ * def f(x: Int) = { def f(x: Int) = new C().f2(x)
+ * trait T { ==> trait T
+ * def f2 = x def f2(x$1: Int) = x$1
+ * } }
+ * class C extends T class C extends T
+ * new C().f2
+ * }
+ */
class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform =>
import LambdaLift._
import ast.tpd._
@@ -67,9 +98,14 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
* For methods and classes that do not have any dependencies this will be the enclosing package.
* symbols with packages as lifted owners will subsequently represented as static
* members of their toplevel class, unless their enclosing class was already static.
+ * Note: During tree transform (which runs at phase LambdaLift + 1), liftedOwner
+ * is also used to decide whether a method had a term owner before.
*/
private val liftedOwner = new HashMap[Symbol, Symbol]
+ /** The outer parameter of a constructor */
+ private val outerParam = new HashMap[Symbol, Symbol]
+
/** Buffers for lifted out classes and methods, indexed by owner */
private val liftedDefs = new HashMap[Symbol, mutable.ListBuffer[Tree]]
@@ -85,15 +121,26 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet =
f.getOrElseUpdate(sym, newSymSet)
- def proxies(sym: Symbol): List[Symbol] = {
- val pm: Map[Symbol, Symbol] = proxyMap.getOrElse(sym, Map.empty) // Dotty deviation: Type annotation needed. TODO: figure out why
- free.getOrElse(sym, Nil).toList.map(pm)
+ def freeVars(sym: Symbol): List[Symbol] = free.getOrElse(sym, Nil).toList
+
+ def proxyOf(sym: Symbol, fv: Symbol) = proxyMap.getOrElse(sym, Map.empty)(fv)
+
+ def proxies(sym: Symbol): List[Symbol] = freeVars(sym).map(proxyOf(sym, _))
+
+ /** A symbol is local if it is owned by a term or a local trait,
+ * or if it is a constructor of a local symbol.
+ */
+ def isLocal(sym: Symbol)(implicit ctx: Context): Boolean = {
+ val owner = sym.maybeOwner
+ owner.isTerm ||
+ owner.is(Trait) && isLocal(owner) ||
+ sym.isConstructor && isLocal(owner)
}
/** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested
* than the previous value of `liftedowner(sym)`.
*/
- def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = {
+ def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) =
if (sym.maybeOwner.isTerm &&
owner.isProperlyContainedIn(liftedOwner(sym)) &&
owner != sym) {
@@ -101,7 +148,6 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
changedLiftedOwner = true
liftedOwner(sym) = owner
}
- }
/** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined
* in `enclosure` or there is an intermediate class properly containing `enclosure`
@@ -116,10 +162,13 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
* 2. If there is no intermediate class, `enclosure` must be contained
* in the class enclosing `sym`.
*
- * Return the closest enclosing intermediate class between `enclosure` and
- * the owner of sym, or NoSymbol if none exists.
+ * @return If there is a non-trait class between `enclosure` and
+ * the owner of `sym`, the largest such class.
+ * Otherwise, if there is a trait between `enclosure` and
+ * the owner of `sym`, the largest such trait.
+ * Otherwise, NoSymbol.
*
- * pre: sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass)
+ * @pre sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass)
*
* The idea of `markFree` is illustrated with an example:
*
@@ -148,27 +197,26 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
if (!enclosure.exists) throw new NoPath
if (enclosure == sym.enclosure) NoSymbol
else {
- ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
- ctx.debuglog(i"$enclosure != ${sym.enclosure}")
+ ctx.debuglog(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
val intermediate =
if (enclosure.is(PackageClass)) enclosure
- else markFree(sym, enclosure.skipConstructor.enclosure)
- // `enclosure` might be a constructor, in which case we want the enclosure
- // of the enclosing class, so skipConstructor is needed here.
- if (intermediate.exists) {
- narrowLiftedOwner(enclosure, intermediate)
- intermediate
- }
- else {
- narrowLiftedOwner(enclosure, sym.enclosingClass)
- val ss = symSet(free, enclosure)
- if (!ss(sym)) {
- ss += sym
- changedFreeVars = true
- ctx.debuglog(i"$sym is free in $enclosure")
- }
- if (enclosure.isClass) enclosure else NoSymbol
+ else markFree(sym, enclosure.enclosure)
+ narrowLiftedOwner(enclosure, intermediate orElse sym.enclosingClass)
+ if (!intermediate.isRealClass || enclosure.isConstructor) {
+ // Constructors and methods nested inside traits get the free variables
+ // of the enclosing trait or class.
+ // Conversely, local traits do not get free variables.
+ if (!enclosure.is(Trait))
+ if (symSet(free, enclosure).add(sym)) {
+ changedFreeVars = true
+ ctx.log(i"$sym is free in $enclosure")
+ }
}
+ if (intermediate.isRealClass) intermediate
+ else if (enclosure.isRealClass) enclosure
+ else if (intermediate.isClass) intermediate
+ else if (enclosure.isClass) enclosure
+ else NoSymbol
}
} catch {
case ex: NoPath =>
@@ -177,15 +225,14 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
}
private def markCalled(callee: Symbol, caller: Symbol)(implicit ctx: Context): Unit = {
- ctx.debuglog(i"mark called: $callee of ${callee.owner} is called by $caller")
- assert(callee.skipConstructor.owner.isTerm)
+ ctx.debuglog(i"mark called: $callee of ${callee.owner} is called by $caller in ${caller.owner}")
+ assert(isLocal(callee))
symSet(called, caller) += callee
if (callee.enclosingClass != caller.enclosingClass) calledFromInner += callee
}
private class CollectDependencies extends EnclosingMethodTraverser {
- def traverse(enclMeth: Symbol, tree: Tree)(implicit ctx: Context) = try { //debug
- val enclosure = enclMeth.skipConstructor
+ def traverse(enclosure: Symbol, tree: Tree)(implicit ctx: Context) = try { //debug
val sym = tree.symbol
def narrowTo(thisClass: ClassSymbol) = {
val enclClass = enclosure.enclosingClass
@@ -195,17 +242,16 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
}
tree match {
case tree: Ident =>
- if (sym.maybeOwner.isTerm) {
+ if (isLocal(sym)) {
if (sym is Label)
assert(enclosure == sym.enclosure,
i"attempt to refer to label $sym from nested $enclosure")
else if (sym is Method) markCalled(sym, enclosure)
else if (sym.isTerm) markFree(sym, enclosure)
- } else if (sym.maybeOwner.isClass)
- narrowTo(sym.owner.asClass)
+ }
+ if (sym.maybeOwner.isClass) narrowTo(sym.owner.asClass)
case tree: Select =>
- if (sym.isConstructor && sym.owner.owner.isTerm)
- markCalled(sym, enclosure)
+ if (sym.is(Method) && isLocal(sym)) markCalled(sym, enclosure)
case tree: This =>
narrowTo(tree.symbol.asClass)
case tree: DefDef =>
@@ -216,8 +262,18 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
// On the other hand, all other methods will be indirectly owned by their
// top-level class. This avoids possible deadlocks when a static method
// has to access its enclosing object from the outside.
- else if (sym.isPrimaryConstructor && sym.owner.owner.isTerm)
- symSet(called, sym) += sym.owner
+ else if (sym.isConstructor) {
+ if (sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait))
+ // add a call edge from the constructor of a local non-trait class to
+ // the class itself. This is done so that the constructor inherits
+ // the free variables of the class.
+ symSet(called, sym) += sym.owner
+
+ tree.vparamss.head.find(_.name == nme.OUTER) match {
+ case Some(vdef) => outerParam(sym) = vdef.symbol
+ case _ =>
+ }
+ }
case tree: TypeDef =>
if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner
case tree: Template =>
@@ -251,7 +307,19 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
for {
caller <- called.keys
callee <- called(caller)
- } narrowLiftedOwner(caller, liftedOwner(callee.skipConstructor))
+ } {
+ val normalizedCallee = callee.skipConstructor
+ val calleeOwner = normalizedCallee.owner
+ if (calleeOwner.isTerm) narrowLiftedOwner(caller, liftedOwner(normalizedCallee))
+ else {
+ assert(calleeOwner.is(Trait))
+ // methods nested inside local trait methods cannot be lifted out
+ // beyond the trait. Note that we can also call a trait method through
+ // a qualifier; in that case no restriction to lifted owner arises.
+ if (caller.isContainedIn(calleeOwner))
+ narrowLiftedOwner(caller, calleeOwner)
+ }
+ }
} while (changedLiftedOwner)
private def newName(sym: Symbol)(implicit ctx: Context): Name =
@@ -275,7 +343,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
private def liftedInfo(local: Symbol)(implicit ctx: Context): Type = local.info match {
case mt @ MethodType(pnames, ptypes) =>
- val ps = proxies(local.skipConstructor)
+ val ps = proxies(local)
MethodType(
ps.map(_.name.asTermName) ++ pnames,
ps.map(_.info) ++ ptypes,
@@ -304,14 +372,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
initFlags = local.flags &~ (InSuperCall | Module) | Private | maybeStatic,
// drop Module because class is no longer a singleton in the lifted context.
info = liftedInfo(local)).installAfter(thisTransform)
- if (local.isClass)
- for (member <- local.asClass.info.decls)
- if (member.isConstructor) {
- val linfo = liftedInfo(member)
- if (linfo ne member.info)
- member.copySymDenotation(info = linfo).installAfter(thisTransform)
- }
}
+ for (local <- free.keys)
+ if (!liftedOwner.contains(local))
+ local.copySymDenotation(info = liftedInfo(local)).installAfter(thisTransform)
}
private def init(implicit ctx: Context) = {
@@ -329,16 +393,17 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
}
private def currentEnclosure(implicit ctx: Context) =
- ctx.owner.enclosingMethod.skipConstructor
+ ctx.owner.enclosingMethodOrClass
private def inCurrentOwner(sym: Symbol)(implicit ctx: Context) =
sym.enclosure == currentEnclosure
private def proxy(sym: Symbol)(implicit ctx: Context): Symbol = {
+ def liftedEnclosure(sym: Symbol) = liftedOwner.getOrElse(sym, sym.enclosure)
def searchIn(enclosure: Symbol): Symbol = {
if (!enclosure.exists) {
def enclosures(encl: Symbol): List[Symbol] =
- if (encl.exists) encl :: enclosures(encl.enclosure) else Nil
+ if (encl.exists) encl :: enclosures(liftedEnclosure(encl)) else Nil
throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, encl = $currentEnclosure, owners = ${currentEnclosure.ownersIterator.toList}%, %; enclosures = ${enclosures(currentEnclosure)}%, %")
}
ctx.debuglog(i"searching for $sym(${sym.owner}) in $enclosure")
@@ -350,7 +415,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
}
case none =>
}
- searchIn(enclosure.enclosure)
+ searchIn(liftedEnclosure(enclosure))
}
if (inCurrentOwner(sym)) sym else searchIn(currentEnclosure)
}
@@ -358,7 +423,13 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
private def memberRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = {
val clazz = sym.enclosingClass
val qual =
- if (clazz.isStaticOwner) singleton(clazz.thisType)
+ if (clazz.isStaticOwner || ctx.owner.enclosingClass == clazz)
+ singleton(clazz.thisType)
+ else if (ctx.owner.isConstructor)
+ outerParam.get(ctx.owner) match {
+ case Some(param) => outer.path(clazz, Ident(param.termRef))
+ case _ => outer.path(clazz)
+ }
else outer.path(clazz)
transformFollowingDeep(qual.select(sym))
}
@@ -378,35 +449,27 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
case Nil => tree
case proxies =>
val sym = tree.symbol
- val ownProxies =
- if (!sym.isConstructor) proxies
- else proxies.map(_.copy(owner = sym, flags = Synthetic | Param))
- val freeParamDefs = ownProxies.map(proxy =>
+ val freeParamDefs = proxies.map(proxy =>
transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef])
def proxyInit(field: Symbol, param: Symbol) =
transformFollowingDeep(memberRef(field).becomes(ref(param)))
- /** Map references to proxy fields `this.proxy` to proxy parameters */
- def mapProxies = new TreeMap {
- override def transform(tree: Tree)(implicit ctx: Context) = tree match {
- case Select(This(_), _) if proxies contains tree.symbol =>
- ref(tree.symbol.subst(proxies, ownProxies))
- case _ =>
- super.transform(tree)
- }
- }
-
/** Initialize proxy fields from proxy parameters and map `rhs` from fields to parameters */
def copyParams(rhs: Tree) = {
- ctx.log(i"copy params ${proxies.map(_.showLocated)}%, %, own = ${ownProxies.map(_.showLocated)}%, %")
- seq((proxies, ownProxies).zipped.map(proxyInit), mapProxies.transform(rhs))
+ val fvs = freeVars(sym.owner)
+ val classProxies = fvs.map(proxyOf(sym.owner, _))
+ val constrProxies = fvs.map(proxyOf(sym, _))
+ ctx.debuglog(i"copy params ${constrProxies.map(_.showLocated)}%, % to ${classProxies.map(_.showLocated)}%, %}")
+ seq((classProxies, constrProxies).zipped.map(proxyInit), rhs)
}
tree match {
case tree: DefDef =>
cpy.DefDef(tree)(
vparamss = tree.vparamss.map(freeParamDefs ++ _),
- rhs = if (sym.isPrimaryConstructor) copyParams(tree.rhs) else tree.rhs)
+ rhs =
+ if (sym.isPrimaryConstructor && !sym.owner.is(Trait)) copyParams(tree.rhs)
+ else tree.rhs)
case tree: Template =>
cpy.Template(tree)(body = freeParamDefs ++ tree.body)
}
@@ -439,19 +502,18 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
}
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
- cpy.Apply(tree)(tree.fun, addFreeArgs(tree.symbol.skipConstructor, tree.args)).withPos(tree.pos)
+ cpy.Apply(tree)(tree.fun, addFreeArgs(tree.symbol, tree.args)).withPos(tree.pos)
override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) =
cpy.Closure(tree)(env = addFreeArgs(tree.meth.symbol, tree.env))
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
val sym = tree.symbol
- val proxyHolder = sym.skipConstructor
- if (needsLifting(proxyHolder)) {
- val paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef]
- if (sym.isConstructor) paramsAdded else liftDef(paramsAdded)
- }
- else tree
+ val paramsAdded =
+ if (free.contains(sym)) addFreeParams(tree, proxies(sym)).asInstanceOf[DefDef]
+ else tree
+ if (needsLifting(sym)) liftDef(paramsAdded)
+ else paramsAdded
}
override def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo) = tree.expr match {
diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala
index 9d4fa9788..05305575e 100644
--- a/src/dotty/tools/dotc/transform/SymUtils.scala
+++ b/src/dotty/tools/dotc/transform/SymUtils.scala
@@ -53,10 +53,16 @@ class SymUtils(val self: Symbol) extends AnyVal {
final def skipConstructor(implicit ctx: Context): Symbol =
if (self.isConstructor) self.owner else self
- /** The logically enclosing method or class for this symbol.
- * Instead of constructors one always picks the enclosing class.
- */
- final def enclosure(implicit ctx: Context) = self.owner.enclosingMethod.skipConstructor
+ /** The closest properly enclosing method or class of this symbol. */
+ final def enclosure(implicit ctx: Context) = {
+ self.owner.enclosingMethodOrClass
+ }
+
+ /** The closest enclosing method or class of this symbol */
+ final def enclosingMethodOrClass(implicit ctx: Context): Symbol =
+ if (self.is(Method, butNot = Label) || self.isClass) self
+ else if (self.exists) self.owner.enclosingMethodOrClass
+ else NoSymbol
/** Apply symbol/symbol substitution to this symbol */
def subst(from: List[Symbol], to: List[Symbol]): Symbol = {