Skip to content

Commit 9c668f3

Browse files
committed
A version of transformStats that avoids stack overflows
1 parent 1bc6cb9 commit 9c668f3

File tree

1 file changed

+21
-20
lines changed

1 file changed

+21
-20
lines changed

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

+21-20
Original file line numberDiff line numberDiff line change
@@ -1164,37 +1164,38 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11641164
* in the same way as Typer does. The code addresses additional concerns:
11651165
* - be tail-recursive where possible
11661166
* - don't re-allocate trees where nothing has changed
1167+
* - avoid stack overflows for long statement lists
11671168
*/
11681169
override def transformStats(stats: List[Tree], exprOwner: Symbol)(using Context): List[Tree] =
1169-
1170-
@tailrec def traverse(curStats: List[Tree])(using Context): List[Tree] =
1171-
1172-
def recur(stats: List[Tree], changed: Tree, rest: List[Tree])(using Context): List[Tree] =
1173-
if stats eq curStats then
1174-
val rest1 = transformStats(rest, exprOwner)
1175-
changed match
1176-
case Thicket(trees) => trees ::: rest1
1177-
case tree => tree :: rest1
1178-
else
1179-
stats.head :: recur(stats.tail, changed, rest)
1180-
1181-
curStats match
1170+
@tailrec
1171+
def loop(mapped: mutable.ListBuffer[Tree] | Null, unchanged: List[Tree], pending: List[Tree])(using Context): List[Tree] =
1172+
pending match
11821173
case stat :: rest =>
1183-
val statCtx = stat match
1174+
def statCtx = stat match
11841175
case _: DefTree | _: ImportOrExport => ctx
11851176
case _ => ctx.exprContext(stat, exprOwner)
1186-
val restCtx = stat match
1177+
def restCtx = stat match
11871178
case stat: Import => ctx.importContext(stat, stat.symbol)
11881179
case _ => ctx
11891180
val stat1 = transform(stat)(using statCtx)
1190-
if stat1 ne stat then recur(stats, stat1, rest)(using restCtx)
1191-
else traverse(rest)(using restCtx)
1181+
if stat1 eq stat then
1182+
loop(mapped, unchanged, rest)
1183+
else
1184+
val buf = if mapped == null then new mutable.ListBuffer[Tree] else mapped
1185+
var xc = unchanged
1186+
while xc ne pending do
1187+
buf += xc.head
1188+
xc = xc.tail
1189+
stat1 match
1190+
case Thicket(stats1) => buf ++= stats1
1191+
case _ => buf += stat1
1192+
loop(buf, rest, rest)(using restCtx)
11921193
case nil =>
1193-
stats
1194+
if mapped == null then unchanged
1195+
else mapped.prependToList(unchanged)
11941196

1195-
traverse(stats)
1197+
loop(null, stats, stats)
11961198
end transformStats
1197-
11981199
end TreeMapWithPreciseStatContexts
11991200

12001201
/** Map Inlined nodes, NamedArgs, Blocks with no statements and local references to underlying arguments.

0 commit comments

Comments
 (0)