diff options
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/core/NameOps.scala | 7 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/Erasure.scala | 2 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala | 25 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/LambdaLift.scala | 6 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 6 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Inliner.scala | 15 | ||||
-rw-r--r-- | tests/pos/i1990.scala | 12 | ||||
-rw-r--r-- | tests/pos/i1990a.scala | 20 | ||||
-rw-r--r-- | tests/run/builder.check | 1 | ||||
-rw-r--r-- | tests/run/builder.scala | 51 | ||||
-rw-r--r-- | tests/run/i1990b.check | 1 | ||||
-rw-r--r-- | tests/run/i1990b.scala | 20 |
13 files changed, 149 insertions, 19 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d1d886c55..e5904156f 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -342,7 +342,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { private def followOuterLinks(t: Tree)(implicit ctx: Context) = t match { case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) => // after erasure outer paths should be respected - new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol) + new ExplicitOuter.OuterOps(ctx).path(toCls = t.tpe.widen.classSymbol) case t => t } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index cd3ae2a25..aac313892 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -271,6 +271,13 @@ object NameOps { else -1 } + + /** The number of hops specified in an outer-select name */ + def outerSelectHops: Int = { + require(isOuterSelect) + name.dropRight(nme.OUTER_SELECT.length).toString.toInt + } + /** The name of the generic runtime operation corresponding to an array operation */ def genericArrayOp: TermName = name match { case nme.apply => nme.array_apply diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 34ea2bc6f..3857b405f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -415,7 +415,7 @@ object Erasure extends TypeTestsCasts{ if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree) else { ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}") - outer.path(tree.symbol) + outer.path(toCls = tree.symbol) } private def runtimeCallWithProtoArgs(name: Name, pt: Type, args: Tree*)(implicit ctx: Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index c2aacf826..d75c32fcc 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -60,7 +60,8 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf /** Convert a selection of the form `qual.C_<OUTER>` to an outer path from `qual` to `C` */ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = if (tree.name.isOuterSelect) - outer.path(tree.tpe.widen.classSymbol, tree.qualifier).ensureConforms(tree.tpe) + outer.path(start = tree.qualifier, count = tree.name.outerSelectHops) + .ensureConforms(tree.tpe) else tree /** First, add outer accessors if a class does not have them yet and it references an outer this. @@ -354,24 +355,32 @@ object ExplicitOuter { } else Nil } - /** The path of outer accessors that references `toCls.this` starting from - * the context owner's this node. + /** A path of outer accessors starting from node `start`. `start` defaults to the + * context owner's this node. There are two alternative conditions that determine + * where the path ends: + * + * - if the initial `count` parameter is non-negative: where the number of + * outer accessors reaches count. + * - if the initial `count` parameter is negative: where the class symbol of + * the type of the reached tree matches `toCls`. */ - def path(toCls: Symbol, start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass)): Tree = try { - def loop(tree: Tree): Tree = { + def path(start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass), + toCls: Symbol = NoSymbol, + count: Int = -1): Tree = try { + def loop(tree: Tree, count: Int): 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 ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls") - if (treeCls == toCls) tree + if (count == 0 || count < 0 && treeCls == toCls) tree else { val acc = outerAccessor(treeCls.asClass)(outerAccessorCtx) assert(acc.exists, i"failure to construct path from ${ctx.owner.ownersIterator.toList}%/% to `this` of ${toCls.showLocated};\n${treeCls.showLocated} does not have an outer accessor") - loop(tree.select(acc).ensureApplied) + loop(tree.select(acc).ensureApplied, count - 1) } } ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}") - loop(start) + loop(start, count) } catch { case ex: ClassCastException => throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls") diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 19fb3dd0c..b603a53dc 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -440,10 +440,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform 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) + case Some(param) => outer.path(start = Ident(param.termRef), toCls = clazz) + case _ => outer.path(toCls = clazz) } - else outer.path(clazz) + else outer.path(toCls = clazz) transformFollowingDeep(qual.select(sym)) } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index b2b99160b..7a4af647f 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -12,6 +12,7 @@ import core.Types._ import core.Flags._ import core.Constants._ import core.StdNames._ +import core.NameOps._ import core.Decorators._ import core.TypeErasure.isErasedType import core.Phases.Phase @@ -336,7 +337,10 @@ class TreeChecker extends Phase with SymTransformer { assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) val tpe = tree.typeOpt val sym = tree.symbol - if (!tpe.isInstanceOf[WithFixedSym] && sym.exists && !sym.is(Private)) { + if (!tpe.isInstanceOf[WithFixedSym] && + sym.exists && !sym.is(Private) && + !tree.name.isOuterSelect // outer selects have effectively fixed symbols + ) { val qualTpe = tree.qualifier.typeOpt val member = if (sym.is(Private)) qualTpe.member(tree.name) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 3fa39edd5..27c7d0c2f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -399,24 +399,29 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol // The name of the outer selector that computes the rhs of `selfSym` - def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT + def outerSelector(n: Int): TermName = n.toString.toTermName ++ nme.OUTER_SELECT // The total nesting depth of the class represented by `selfSym`. def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length - // All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first) - val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_)) + // All needed this-proxies, paired-with and sorted-by nesting depth of + // the classes they represent (innermost first) + val sortedProxies = thisProxy.toList.map { + case (cls, proxy) => (outerLevel(cls), proxy.symbol) + } sortBy (-_._1) // Compute val-definitions for all this-proxies and append them to `bindingsBuf` var lastSelf: Symbol = NoSymbol - for (selfSym <- accessedSelfSyms) { + var lastLevel: Int = 0 + for ((level, selfSym) <- sortedProxies) { val rhs = if (!lastSelf.exists) prefix else - untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info) + untpd.Select(ref(lastSelf), outerSelector(lastLevel - level)).withType(selfSym.info) bindingsBuf += ValDef(selfSym.asTerm, rhs) lastSelf = selfSym + lastLevel = level } // The type map to apply to the inlined tree. This maps references to this-types diff --git a/tests/pos/i1990.scala b/tests/pos/i1990.scala new file mode 100644 index 000000000..77cea0af7 --- /dev/null +++ b/tests/pos/i1990.scala @@ -0,0 +1,12 @@ +class A { + class Foo { + inline def inlineMeth: Unit = { + new Bar + } + } + class Bar +} + +class B extends A { + (new Foo).inlineMeth +} diff --git a/tests/pos/i1990a.scala b/tests/pos/i1990a.scala new file mode 100644 index 000000000..f6f95ee36 --- /dev/null +++ b/tests/pos/i1990a.scala @@ -0,0 +1,20 @@ +class A { self => + class Foo { + inline def inlineMeth: Unit = { + println(self) + } + } +} + +class C extends A { + class B extends A +} + +object Test { + def main(args: Array[String]): Unit = { + val c = new C + val b = new c.B + + (new b.Foo).inlineMeth + } +} diff --git a/tests/run/builder.check b/tests/run/builder.check new file mode 100644 index 000000000..48f7d9253 --- /dev/null +++ b/tests/run/builder.check @@ -0,0 +1 @@ +Table(Row(Cell(A1), Cell(B1)), Row(Cell(A2), Cell(B2))) diff --git a/tests/run/builder.scala b/tests/run/builder.scala new file mode 100644 index 000000000..532a95071 --- /dev/null +++ b/tests/run/builder.scala @@ -0,0 +1,51 @@ +import collection.mutable.ArrayBuffer + +class Table { + val rows = new ArrayBuffer[Row] + def add(r: Row): Unit = rows += r + override def toString = rows.mkString("Table(", ", ", ")") +} + +class Row { + val cells = new ArrayBuffer[Cell] + def add(c: Cell): Unit = cells += c + override def toString = cells.mkString("Row(", ", ", ")") +} + +class Cell(elem: String) { + override def toString = s"Cell($elem)" +} + +object Test { + + def table(init: implicit Table => Unit) = { + implicit val t = new Table + init + t + } + + def row(init: implicit Row => Unit)(implicit t: Table) = { + implicit val r = new Row + init + t.add(r) + } + + def cell(str: String)(implicit r: Row) = + r.add(new Cell(str)) + + val data = + table { + row { + cell("A1") + cell("B1") + } + row { + cell("A2") + cell("B2") + } + } + + def main(args: Array[String]) = { + println(data) + } +} diff --git a/tests/run/i1990b.check b/tests/run/i1990b.check new file mode 100644 index 000000000..dd84bc9fb --- /dev/null +++ b/tests/run/i1990b.check @@ -0,0 +1 @@ +C() diff --git a/tests/run/i1990b.scala b/tests/run/i1990b.scala new file mode 100644 index 000000000..2460208db --- /dev/null +++ b/tests/run/i1990b.scala @@ -0,0 +1,20 @@ +trait A { self => + class Foo { + inline def inlineMeth: Unit = { + println(self) + } + } +} + +case class A2() extends A { + case class C() extends Foo with A + + val c = new C + (new c.Foo).inlineMeth +} + +object Test { + def main(args: Array[String]): Unit = { + new A2 + } +} |