Skip to content

Commit d27adbe

Browse files
committed
Correct owner chains when mixing byname implicits and inlining
When constructing the dictionary for a byname implicit resolution we have to ensure that the owners of any definitions which have been hoisted into the dictionary are correct. Prior to this PR this was only done fully for implicit expansions which didn't involve inlining. We fix that here by ensuring that all definitions under a given dictionary entry are owned directly or indirectly by that dictionary entry. Fixes scala#5766.
1 parent ab091cf commit d27adbe

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,28 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
730730
if (from == to) tree else loop(from, Nil, to :: Nil)
731731
}
732732

733+
/**
734+
* Set the owner of every definition in this tree which is not itself contained in this
735+
* tree to be `newowner`
736+
*/
737+
def changeNonLocalOwners(newowner: Symbol)(implicit ctx: Context): Tree = {
738+
val localOwners = mutable.HashSet[Symbol]()
739+
val traverser = new TreeTraverser {
740+
741+
def traverse(tree: Tree)(implicit ctx: Context): Unit = {
742+
tree match {
743+
case _: DefTree => if(tree.symbol ne NoSymbol) localOwners += tree.symbol
744+
case _ =>
745+
}
746+
traverseChildren(tree)
747+
}
748+
}
749+
traverser.traverse(tree)
750+
val nonLocalOwners = localOwners.filter(sym => !localOwners.contains(sym.owner)).toList.map(_.owner)
751+
val newOwners = nonLocalOwners.map(_ => newowner)
752+
new TreeTypeMap(oldOwners = nonLocalOwners, newOwners = newOwners).apply(tree)
753+
}
754+
733755
/** After phase `trans`, set the owner of every definition in this tree that was formerly
734756
* owner by `from` to `to`.
735757
*/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1577,7 +1577,7 @@ final class SearchRoot extends SearchHistory {
15771577
val nrhss = rhss.map(rhsMap(_))
15781578

15791579
val vdefs = (nsyms zip nrhss) map {
1580-
case (nsym, nrhs) => ValDef(nsym.asTerm, nrhs)
1580+
case (nsym, nrhs) => ValDef(nsym.asTerm, nrhs.changeNonLocalOwners(nsym))
15811581
}
15821582

15831583
val constr = ctx.newConstructor(classSym, Synthetic, Nil, Nil).entered

tests/pos/i5766.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
object Test1 {
2+
trait Foo { def next: Foo }
3+
4+
inline implicit def foo(implicit loop: => Foo): Foo = new Foo { def next = loop }
5+
6+
def summon(implicit f: Foo) = ()
7+
summon
8+
}
9+
10+
object Test2 {
11+
trait Foo { def next: Bar }
12+
trait Bar { def next: Foo }
13+
14+
implicit def foo(implicit loop: => Bar): Foo = new Foo { def next = loop }
15+
inline implicit def bar(implicit loop: Foo): Bar = new Bar { def next = loop }
16+
17+
def summon(implicit f: Foo) = ()
18+
summon
19+
}
20+
21+
object Test3 {
22+
trait Foo { def next: Bar }
23+
trait Bar { def next: Baz }
24+
trait Baz { def next: Foo }
25+
26+
implicit def foo(implicit loop: Bar): Foo = new Foo { def next = loop }
27+
inline implicit def bar(implicit loop: Baz): Bar = new Bar { def next = loop }
28+
inline implicit def baz(implicit loop: => Foo): Baz = new Baz { def next = loop }
29+
30+
def summon(implicit f: Foo) = ()
31+
summon
32+
}

0 commit comments

Comments
 (0)