aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/dotc/ast/tpd.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/NameOps.scala7
-rw-r--r--compiler/src/dotty/tools/dotc/transform/Erasure.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala25
-rw-r--r--compiler/src/dotty/tools/dotc/transform/LambdaLift.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/transform/TreeChecker.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Inliner.scala15
-rw-r--r--tests/pos/i1990.scala12
-rw-r--r--tests/pos/i1990a.scala20
-rw-r--r--tests/run/builder.check1
-rw-r--r--tests/run/builder.scala51
-rw-r--r--tests/run/i1990b.check1
-rw-r--r--tests/run/i1990b.scala20
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 cfc0003c6..fde304c69 100644
--- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
@@ -396,24 +396,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
+ }
+}