Skip to content

Commit eb772ae

Browse files
Do not inline val across class boundaries
1 parent 82bf1bb commit eb772ae

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

compiler/src/dotty/tools/dotc/transform/linker/Simplify.scala

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,6 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
837837
// invalidates the denotation cache. Because this optimization only
838838
// operates locally, this should be fine.
839839
val denot = app.fun.symbol.denot
840-
simplify.println(s"replacing ${app.symbol}")
841840
if (!denot.info.finalResultType.derivesFrom(defn.UnitClass)) {
842841
val newLabelType = app.symbol.info match {
843842
case mt: MethodType =>
@@ -1113,10 +1112,11 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
11131112
val devalify: Optimization = { implicit ctx: Context =>
11141113
val timesUsed = mutable.HashMap[Symbol, Int]()
11151114
val defined = mutable.HashSet[Symbol]()
1115+
val usedInInnerClass = mutable.HashMap[Symbol, Int]()
11161116
// Either a duplicate or a read through series of immutable fields
11171117
val copies = mutable.HashMap[Symbol, Tree]()
1118-
val visitor: Visitor = {
1119-
case valdef: ValDef if !valdef.symbol.is(Param) &&
1118+
def doVisit(tree: Tree, used: mutable.HashMap[Symbol, Int]): Unit = tree match {
1119+
case valdef: ValDef if !valdef.symbol.is(Param) &&
11201120
!valdef.symbol.is(Mutable | Module | Lazy) &&
11211121
valdef.symbol.exists && !valdef.symbol.owner.isClass =>
11221122
defined += valdef.symbol
@@ -1128,20 +1128,45 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
11281128
}
11291129
case t: New =>
11301130
val symIfExists = t.tpt.tpe.normalizedPrefix.termSymbol
1131-
val b4 = timesUsed.getOrElseUpdate(symIfExists, 0)
1132-
timesUsed.put(symIfExists, b4 + 1)
1131+
val b4 = used.getOrElseUpdate(symIfExists, 0)
1132+
used.put(symIfExists, b4 + 1)
11331133

11341134
case valdef: ValDef if valdef.symbol.exists && !valdef.symbol.owner.isClass &&
11351135
!valdef.symbol.is(Param | Module | Lazy) =>
11361136
// TODO: handle params after constructors. Start changing public signatures by eliminating unused arguments.
11371137
defined += valdef.symbol
11381138

11391139
case t: RefTree =>
1140-
val b4 = timesUsed.getOrElseUpdate(t.symbol, 0)
1141-
timesUsed.put(t.symbol, b4 + 1)
1140+
val b4 = used.getOrElseUpdate(t.symbol, 0)
1141+
used.put(t.symbol, b4 + 1)
11421142
case _ =>
11431143
}
11441144

1145+
val visitor: Visitor = { tree =>
1146+
def crossingClassBoundaries(t: Tree): Boolean = t match {
1147+
case _: New => true
1148+
case _: Template => true
1149+
case _ => false
1150+
}
1151+
// We shouldn't inline `This` nodes, which we approximate by not inlining
1152+
// anything across class boundaries. To do so, we visit every class a
1153+
// second time and record what's used in the usedInInnerClass Set.
1154+
if (crossingClassBoundaries(tree)) {
1155+
// Doing a foreachSubTree(tree) here would work, but would also
1156+
// be exponential for deeply nested classes. Instead we do a short
1157+
// circuit traversal that doesn't visit further nested classes.
1158+
val reVisitClass = new TreeAccumulator[Unit] {
1159+
def apply(u: Unit, t: Tree)(implicit ctx: Context): Unit = {
1160+
doVisit(t, usedInInnerClass)
1161+
if (!crossingClassBoundaries(t))
1162+
foldOver((), t)
1163+
}
1164+
}
1165+
reVisitClass.foldOver((), tree)
1166+
}
1167+
doVisit(tree, timesUsed)
1168+
}
1169+
11451170
val transformer: Transformer = () => localCtx => {
11461171
val valsToDrop = defined -- timesUsed.keySet
11471172
val copiesToReplaceAsDuplicates = copies.filter { x =>
@@ -1157,7 +1182,7 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {
11571182
case None => Nil
11581183
})
11591184

1160-
val replacements = copiesToReplaceAsDuplicates ++ copiesToReplaceAsUsedOnce
1185+
val replacements = copiesToReplaceAsDuplicates ++ copiesToReplaceAsUsedOnce -- usedInInnerClass.keySet
11611186

11621187
val deepReplacer = new TreeMap() {
11631188
override def transform(tree: Tree)(implicit ctx: Context): Tree = {

tests/pos/devalify.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,15 @@ object Test {
3131

3232
println(subFooBar)
3333
}
34+
35+
def test3: Unit = {
36+
trait NumericRange {
37+
def mapRange: NumericRange = {
38+
val self = this
39+
new NumericRange {
40+
def underlyingRange: NumericRange = self
41+
}
42+
}
43+
}
44+
}
3445
}

0 commit comments

Comments
 (0)