diff options
author | Martin Odersky <odersky@gmail.com> | 2016-03-02 17:13:16 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-03-02 17:19:03 +0100 |
commit | 69b6b892793e45a7158d006cdfb5554edc5db633 (patch) | |
tree | e9f92408d4903f577b4d0d7c849201b15e45c623 | |
parent | 9d3424da9e34905a20783d4f597d1bb02d0df5d0 (diff) | |
download | dotty-69b6b892793e45a7158d006cdfb5554edc5db633.tar.gz dotty-69b6b892793e45a7158d006cdfb5554edc5db633.tar.bz2 dotty-69b6b892793e45a7158d006cdfb5554edc5db633.zip |
LambdaLift redesign
Simplifications in order to avoid the freqent special
casing of constructors and prepare the way for
proper handling of trait constructors (which cause
problems; see pending/pos/llift.scala.
-rw-r--r-- | src/dotty/tools/dotc/transform/LambdaLift.scala | 75 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/SymUtils.scala | 14 | ||||
-rw-r--r-- | tests/pending/pos/llift.scala | 16 | ||||
-rw-r--r-- | tests/run/llift.scala | 46 |
4 files changed, 102 insertions, 49 deletions
diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 9b0f70160..dfc8c77dc 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -126,10 +126,14 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform free.getOrElse(sym, Nil).toList.map(pm) } - /** A symbol is local if it is owned by a term or a local trait */ + /** 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) + owner.isTerm || + owner.is(Trait) && isLocal(owner) || + sym.isConstructor && isLocal(sym.owner) } /** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested @@ -186,18 +190,17 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform * } * } */ - private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Symbol = try { + private def markFree(sym: Symbol, enclosure: Symbol, propagating: Boolean = false)(implicit ctx: Context): Symbol = try { 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"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. + else markFree(sym, enclosure.enclosure) narrowLiftedOwner(enclosure, intermediate orElse sym.enclosingClass) - if (!intermediate.isClass || intermediate.is(Trait)) { + if (!intermediate.isClass || intermediate.is(Trait) || + enclosure.isConstructor && propagating) { // Methods nested inside traits get the free variables of the enclosing trait. // Conversely, local traits do not get free variables. if (!enclosure.is(Trait)) { @@ -205,7 +208,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform if (!ss(sym)) { ss += sym changedFreeVars = true - ctx.debuglog(i"$sym is free in $enclosure") + ctx.log(i"$sym is free in $enclosure") } } } @@ -221,14 +224,13 @@ 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(isLocal(callee.skipConstructor)) + 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 @@ -237,18 +239,17 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner } tree match { - case tree: RefTree => + case tree: Ident => 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.isConstructor && sym.owner.owner.isTerm) - markCalled(sym, enclosure) } + if (sym.maybeOwner.isClass) narrowTo(sym.owner.asClass) + case tree: Select => + if (sym.is(Method) && isLocal(sym)) markCalled(sym, enclosure) case tree: This => narrowTo(tree.symbol.asClass) case tree: DefDef => @@ -294,7 +295,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform callee <- called(caller) fvs <- free get callee fv <- fvs - } markFree(fv, caller) + } markFree(fv, caller, propagating = true) } while (changedFreeVars) /** Compute final liftedOwner map by closing over caller dependencies */ @@ -340,7 +341,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, @@ -390,16 +391,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") @@ -411,7 +413,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform } case none => } - searchIn(enclosure.enclosure) + searchIn(liftedEnclosure(enclosure)) } if (inCurrentOwner(sym)) sym else searchIn(currentEnclosure) } @@ -445,35 +447,25 @@ 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 classProxies = this.proxies(sym.owner) + ctx.debuglog(i"copy params ${proxies.map(_.showLocated)}%, % to ${classProxies.map(_.showLocated)}%, %}") + seq((classProxies, proxies).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) } @@ -506,16 +498,15 @@ 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 val paramsAdded = - if (free.contains(sym)) addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef] + if (free.contains(sym)) addFreeParams(tree, proxies(sym)).asInstanceOf[DefDef] else tree if (needsLifting(sym)) liftDef(paramsAdded) else paramsAdded 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 = { diff --git a/tests/pending/pos/llift.scala b/tests/pending/pos/llift.scala new file mode 100644 index 000000000..b2a1e163b --- /dev/null +++ b/tests/pending/pos/llift.scala @@ -0,0 +1,16 @@ +object Test { + def f1d(x: Int) = { + trait T1 { +// def f2 = { + trait T2 { + def f3: Int = x + } + class C2 extends T2 + new C2().f3 +// } + def f6 = x + } + class C1 extends T1 + new C1().f6 + } +} diff --git a/tests/run/llift.scala b/tests/run/llift.scala index 0c8c9b4be..28456b8cc 100644 --- a/tests/run/llift.scala +++ b/tests/run/llift.scala @@ -7,6 +7,7 @@ class A { } } } + object Test { def foo(x: Int) = { trait B { @@ -18,7 +19,6 @@ object Test { new C().bar } - def f1(x: Int) = { class C1 { def f2 = { @@ -79,10 +79,11 @@ object Test { def f7 = this.f3 f5 } + def f3a = f3 } class C2 extends T2 class C3 extends T1 - new C2().f3 + new C3().f6 + new C2().f3a + new C3().f6 } def f6 = x } @@ -90,11 +91,50 @@ object Test { new C1().f2 } + def f1d(x: Int) = { + trait T1 { + def f2 = { + trait T2 { + def f3: Int = { + def f4 = x + def f5 = f4 + def f7 = this.f3 + f5 + } + def f3a = f3 + } + class C2 extends T2 + class C3 extends T1 + new C2().f3a + new C3().f6 + } + def f6 = x + } + class C1 extends T1 + new C1().f2 + } + + def f1e(x: Int) = { + trait T1 { + def f2 = { + trait T2 { + def f3: Int = x + } + class C2 extends T2 + new C2().f3 + } + def f6 = x + } + class C1 extends T1 + new C1().f6 + } + def main(args: Array[String]) = { assert(foo(3) == 3) assert(f1(4) == 4) assert(f1a(5) == 5) assert(f1b(6) == 6) - assert(f1c(6) == 12) + assert(f1c(7) == 14) + assert(f1d(8) == 16) + assert(f1e(9) == 9) } } |