Skip to content

Commit 286dafb

Browse files
committed
SI-9050 Fix crasher with value classes, recursion
From the "Substitution is hard to do" department. In 7babdab, TreeSymSubstitutor was modified to mutate the info of symbols defined in the tree, if that symbol's info referred to one of the `from` symbols in the substitution. It would have been more principled to create a cloned symbol with the updated info, and add that to the substitution. But I wasn't able implement that correctly (let alone efficiently.) The in-place mutation of the info of a symbol led to the crasher in this bug: a singleton type over that symbol ends up with a stale cached value of 'underlying'. In the enclosed test case, this leads to a type error in the `SubstituteRecursion` of the extension methods phase. This commit performs a cleanup job at the end of `substituteSymbols` by invalidating the cache of any `SingleType`-s in the tree that refer to one of the mutated symbols.
1 parent 3d76836 commit 286dafb

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

src/reflect/scala/reflect/internal/Trees.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,7 @@ trait Trees extends api.Trees {
15761576
*/
15771577
class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends Transformer {
15781578
val symSubst = new SubstSymMap(from, to)
1579+
private var mutatedSymbols: List[Symbol] = Nil
15791580
override def transform(tree: Tree): Tree = {
15801581
def subst(from: List[Symbol], to: List[Symbol]) {
15811582
if (!from.isEmpty)
@@ -1594,6 +1595,7 @@ trait Trees extends api.Trees {
15941595
|TreeSymSubstituter: updated info of symbol ${tree.symbol}
15951596
| Old: ${showRaw(tree.symbol.info, printTypes = true, printIds = true)}
15961597
| New: ${showRaw(newInfo, printTypes = true, printIds = true)}""")
1598+
mutatedSymbols ::= tree.symbol
15971599
tree.symbol updateInfo newInfo
15981600
}
15991601
case _ =>
@@ -1613,7 +1615,23 @@ trait Trees extends api.Trees {
16131615
} else
16141616
super.transform(tree)
16151617
}
1616-
def apply[T <: Tree](tree: T): T = transform(tree).asInstanceOf[T]
1618+
def apply[T <: Tree](tree: T): T = {
1619+
val tree1 = transform(tree)
1620+
invalidateSingleTypeCaches(tree1)
1621+
tree1.asInstanceOf[T]
1622+
}
1623+
private def invalidateSingleTypeCaches(tree: Tree): Unit = {
1624+
if (mutatedSymbols.nonEmpty)
1625+
for (t <- tree)
1626+
for (tp <- t.tpe) {
1627+
tp match {
1628+
case s: SingleType if mutatedSymbols contains s.sym =>
1629+
s.underlyingPeriod = NoPeriod
1630+
s.underlyingCache = NoType
1631+
case _ =>
1632+
}
1633+
}
1634+
}
16171635
override def toString() = "TreeSymSubstituter/" + substituterString("Symbol", "Symbol", from, to)
16181636
}
16191637

test/files/pos/t9050.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
final class Mu[F](val value: Any) extends AnyVal {
2+
def cata(f: F) {
3+
// crash
4+
((y: Mu[F]) => y.cata(f))
5+
// crash
6+
def foo(x : Mu[F]) = x.cata(f)
7+
8+
// // okay
9+
def x: Mu[F] = ???
10+
(() => x.cata(f))
11+
assert(true, cata(f))
12+
}
13+
}

0 commit comments

Comments
 (0)