Skip to content

Commit 2f25add

Browse files
committed
Replace tpd.mapInline by flattenMapConserve
The new version performs better also for long lists of trees.
1 parent bdfb08f commit 2f25add

File tree

2 files changed

+27
-24
lines changed

2 files changed

+27
-24
lines changed

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

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,35 +1144,38 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11441144
expand(tree, tree.tpe.widen)
11451145
}
11461146

1147-
inline val MapRecursionLimit = 10
1148-
11491147
extension (trees: List[Tree])
11501148

1151-
/** A map that expands to a recursive function. It's equivalent to
1149+
/** Equivalent (but faster) to
11521150
*
11531151
* flatten(trees.mapConserve(op))
11541152
*
1155-
* and falls back to it after `MaxRecursionLimit` recursions.
1156-
* Before that it uses a simpler method that uses stackspace
1157-
* instead of heap.
1158-
* Note `op` is duplicated in the generated code, so it should be
1159-
* kept small.
1153+
* assuming that `trees` does not contain `Thicket`s to start with.
11601154
*/
1161-
inline def mapInline(inline op: Tree => Tree): List[Tree] =
1162-
def recur(trees: List[Tree], count: Int): List[Tree] =
1163-
if count > MapRecursionLimit then
1164-
// use a slower implementation that avoids stack overflows
1165-
flatten(trees.mapConserve(op))
1166-
else trees match
1167-
case tree :: rest =>
1168-
val tree1 = op(tree)
1169-
val rest1 = recur(rest, count + 1)
1170-
if (tree1 eq tree) && (rest1 eq rest) then trees
1171-
else tree1 match
1172-
case Thicket(elems1) => elems1 ::: rest1
1173-
case _ => tree1 :: rest1
1174-
case nil => nil
1175-
recur(trees, 0)
1155+
inline def flattenedMapConserve(inline f: Tree => Tree): List[Tree] =
1156+
@tailrec
1157+
def loop(mapped: ListBuffer[Tree] | Null, unchanged: List[Tree], pending: List[Tree]): List[Tree] =
1158+
if pending.isEmpty then
1159+
if mapped == null then unchanged
1160+
else mapped.prependToList(unchanged)
1161+
else
1162+
val head0 = pending.head
1163+
val head1 = f(head0)
1164+
1165+
if head1 eq head0 then
1166+
loop(mapped, unchanged, pending.tail)
1167+
else
1168+
val buf = if mapped == null then new ListBuffer[Tree] else mapped
1169+
var xc = unchanged
1170+
while xc ne pending do
1171+
buf += xc.head
1172+
xc = xc.tail
1173+
head1 match
1174+
case Thicket(elems1) => buf ++= elems1
1175+
case _ => buf += head1
1176+
val tail0 = pending.tail
1177+
loop(buf, tail0, tail0)
1178+
loop(null, trees, trees)
11761179

11771180
/** Transform statements while maintaining import contexts and expression contexts
11781181
* in the same way as Typer does. The code addresses additional concerns:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
456456
}
457457

458458
def transformTrees(trees: List[Tree], start: Int)(using Context): List[Tree] =
459-
trees.mapInline(transformTree(_, start))
459+
trees.flattenedMapConserve(transformTree(_, start))
460460

461461
def transformSpecificTrees[T <: Tree](trees: List[T], start: Int)(using Context): List[T] =
462462
transformTrees(trees, start).asInstanceOf[List[T]]

0 commit comments

Comments
 (0)