Skip to content

Commit 9f879c1

Browse files
authored
Merge pull request #1996 from dotty-staging/fix-#1990
Fix #1990: Handle inlining where this proxies change types
2 parents f467be6 + bd80a18 commit 9f879c1

File tree

13 files changed

+149
-19
lines changed

13 files changed

+149
-19
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
342342
private def followOuterLinks(t: Tree)(implicit ctx: Context) = t match {
343343
case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) =>
344344
// after erasure outer paths should be respected
345-
new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol)
345+
new ExplicitOuter.OuterOps(ctx).path(toCls = t.tpe.widen.classSymbol)
346346
case t =>
347347
t
348348
}

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,13 @@ object NameOps {
271271
else -1
272272
}
273273

274+
275+
/** The number of hops specified in an outer-select name */
276+
def outerSelectHops: Int = {
277+
require(isOuterSelect)
278+
name.dropRight(nme.OUTER_SELECT.length).toString.toInt
279+
}
280+
274281
/** The name of the generic runtime operation corresponding to an array operation */
275282
def genericArrayOp: TermName = name match {
276283
case nme.apply => nme.array_apply

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ object Erasure extends TypeTestsCasts{
415415
if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree)
416416
else {
417417
ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}")
418-
outer.path(tree.symbol)
418+
outer.path(toCls = tree.symbol)
419419
}
420420

421421
private def runtimeCallWithProtoArgs(name: Name, pt: Type, args: Tree*)(implicit ctx: Context): Tree = {

compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
6060
/** Convert a selection of the form `qual.C_<OUTER>` to an outer path from `qual` to `C` */
6161
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
6262
if (tree.name.isOuterSelect)
63-
outer.path(tree.tpe.widen.classSymbol, tree.qualifier).ensureConforms(tree.tpe)
63+
outer.path(start = tree.qualifier, count = tree.name.outerSelectHops)
64+
.ensureConforms(tree.tpe)
6465
else tree
6566

6667
/** First, add outer accessors if a class does not have them yet and it references an outer this.
@@ -354,24 +355,32 @@ object ExplicitOuter {
354355
} else Nil
355356
}
356357

357-
/** The path of outer accessors that references `toCls.this` starting from
358-
* the context owner's this node.
358+
/** A path of outer accessors starting from node `start`. `start` defaults to the
359+
* context owner's this node. There are two alternative conditions that determine
360+
* where the path ends:
361+
*
362+
* - if the initial `count` parameter is non-negative: where the number of
363+
* outer accessors reaches count.
364+
* - if the initial `count` parameter is negative: where the class symbol of
365+
* the type of the reached tree matches `toCls`.
359366
*/
360-
def path(toCls: Symbol, start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass)): Tree = try {
361-
def loop(tree: Tree): Tree = {
367+
def path(start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass),
368+
toCls: Symbol = NoSymbol,
369+
count: Int = -1): Tree = try {
370+
def loop(tree: Tree, count: Int): Tree = {
362371
val treeCls = tree.tpe.widen.classSymbol
363372
val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore
364373
ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls")
365-
if (treeCls == toCls) tree
374+
if (count == 0 || count < 0 && treeCls == toCls) tree
366375
else {
367376
val acc = outerAccessor(treeCls.asClass)(outerAccessorCtx)
368377
assert(acc.exists,
369378
i"failure to construct path from ${ctx.owner.ownersIterator.toList}%/% to `this` of ${toCls.showLocated};\n${treeCls.showLocated} does not have an outer accessor")
370-
loop(tree.select(acc).ensureApplied)
379+
loop(tree.select(acc).ensureApplied, count - 1)
371380
}
372381
}
373382
ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}")
374-
loop(start)
383+
loop(start, count)
375384
} catch {
376385
case ex: ClassCastException =>
377386
throw new ClassCastException(i"no path exists from ${ctx.owner.enclosingClass} to $toCls")

compiler/src/dotty/tools/dotc/transform/LambdaLift.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
440440
singleton(clazz.thisType)
441441
else if (ctx.owner.isConstructor)
442442
outerParam.get(ctx.owner) match {
443-
case Some(param) => outer.path(clazz, Ident(param.termRef))
444-
case _ => outer.path(clazz)
443+
case Some(param) => outer.path(start = Ident(param.termRef), toCls = clazz)
444+
case _ => outer.path(toCls = clazz)
445445
}
446-
else outer.path(clazz)
446+
else outer.path(toCls = clazz)
447447
transformFollowingDeep(qual.select(sym))
448448
}
449449

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import core.Types._
1212
import core.Flags._
1313
import core.Constants._
1414
import core.StdNames._
15+
import core.NameOps._
1516
import core.Decorators._
1617
import core.TypeErasure.isErasedType
1718
import core.Phases.Phase
@@ -336,7 +337,10 @@ class TreeChecker extends Phase with SymTransformer {
336337
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
337338
val tpe = tree.typeOpt
338339
val sym = tree.symbol
339-
if (!tpe.isInstanceOf[WithFixedSym] && sym.exists && !sym.is(Private)) {
340+
if (!tpe.isInstanceOf[WithFixedSym] &&
341+
sym.exists && !sym.is(Private) &&
342+
!tree.name.isOuterSelect // outer selects have effectively fixed symbols
343+
) {
340344
val qualTpe = tree.qualifier.typeOpt
341345
val member =
342346
if (sym.is(Private)) qualTpe.member(tree.name)

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -396,24 +396,29 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
396396
def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
397397

398398
// The name of the outer selector that computes the rhs of `selfSym`
399-
def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT
399+
def outerSelector(n: Int): TermName = n.toString.toTermName ++ nme.OUTER_SELECT
400400

401401
// The total nesting depth of the class represented by `selfSym`.
402402
def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length
403403

404-
// All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first)
405-
val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_))
404+
// All needed this-proxies, paired-with and sorted-by nesting depth of
405+
// the classes they represent (innermost first)
406+
val sortedProxies = thisProxy.toList.map {
407+
case (cls, proxy) => (outerLevel(cls), proxy.symbol)
408+
} sortBy (-_._1)
406409

407410
// Compute val-definitions for all this-proxies and append them to `bindingsBuf`
408411
var lastSelf: Symbol = NoSymbol
409-
for (selfSym <- accessedSelfSyms) {
412+
var lastLevel: Int = 0
413+
for ((level, selfSym) <- sortedProxies) {
410414
val rhs =
411415
if (!lastSelf.exists)
412416
prefix
413417
else
414-
untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info)
418+
untpd.Select(ref(lastSelf), outerSelector(lastLevel - level)).withType(selfSym.info)
415419
bindingsBuf += ValDef(selfSym.asTerm, rhs)
416420
lastSelf = selfSym
421+
lastLevel = level
417422
}
418423

419424
// The type map to apply to the inlined tree. This maps references to this-types

tests/pos/i1990.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class A {
2+
class Foo {
3+
inline def inlineMeth: Unit = {
4+
new Bar
5+
}
6+
}
7+
class Bar
8+
}
9+
10+
class B extends A {
11+
(new Foo).inlineMeth
12+
}

tests/pos/i1990a.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class A { self =>
2+
class Foo {
3+
inline def inlineMeth: Unit = {
4+
println(self)
5+
}
6+
}
7+
}
8+
9+
class C extends A {
10+
class B extends A
11+
}
12+
13+
object Test {
14+
def main(args: Array[String]): Unit = {
15+
val c = new C
16+
val b = new c.B
17+
18+
(new b.Foo).inlineMeth
19+
}
20+
}

tests/run/builder.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Table(Row(Cell(A1), Cell(B1)), Row(Cell(A2), Cell(B2)))

tests/run/builder.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import collection.mutable.ArrayBuffer
2+
3+
class Table {
4+
val rows = new ArrayBuffer[Row]
5+
def add(r: Row): Unit = rows += r
6+
override def toString = rows.mkString("Table(", ", ", ")")
7+
}
8+
9+
class Row {
10+
val cells = new ArrayBuffer[Cell]
11+
def add(c: Cell): Unit = cells += c
12+
override def toString = cells.mkString("Row(", ", ", ")")
13+
}
14+
15+
class Cell(elem: String) {
16+
override def toString = s"Cell($elem)"
17+
}
18+
19+
object Test {
20+
21+
def table(init: implicit Table => Unit) = {
22+
implicit val t = new Table
23+
init
24+
t
25+
}
26+
27+
def row(init: implicit Row => Unit)(implicit t: Table) = {
28+
implicit val r = new Row
29+
init
30+
t.add(r)
31+
}
32+
33+
def cell(str: String)(implicit r: Row) =
34+
r.add(new Cell(str))
35+
36+
val data =
37+
table {
38+
row {
39+
cell("A1")
40+
cell("B1")
41+
}
42+
row {
43+
cell("A2")
44+
cell("B2")
45+
}
46+
}
47+
48+
def main(args: Array[String]) = {
49+
println(data)
50+
}
51+
}

tests/run/i1990b.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
C()

tests/run/i1990b.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
trait A { self =>
2+
class Foo {
3+
inline def inlineMeth: Unit = {
4+
println(self)
5+
}
6+
}
7+
}
8+
9+
case class A2() extends A {
10+
case class C() extends Foo with A
11+
12+
val c = new C
13+
(new c.Foo).inlineMeth
14+
}
15+
16+
object Test {
17+
def main(args: Array[String]): Unit = {
18+
new A2
19+
}
20+
}

0 commit comments

Comments
 (0)