First wall of the pi coding-agent bring-up (tracker #6564): compiling pi's bundle (13.3MB unminified esbuild ESM output of @earendil-works/pi-coding-agent@0.80.10) crashes the compiler:
thread 'perry-main' (…) has overflowed its stack
fatal runtime error: stack overflow, aborting
Reproduces identically at the stock 128MB perry-main stack AND at a 512MB test bump — so this is effectively unbounded recursion, not a merely-deep finite walk.
Symbolicated crash stack (strip=none rebuild, macOS .ips + nm)
The overflow is the call inliner's mutual recursion, entered from module collection:
36-frame captured window (top of a very deep stack), most frequent:
11x perry_transform::inline::call_inliner::inline_calls_in_expr
9x perry_transform::inline::call_inliner::inline_calls_in_stmts
2x perry_transform::inline::call_inliner::stmt_contains_return
1x perry_transform::inline::inline_functions
1x perry::commands::compile::collect_modules::collect_module_finish
Root cause
inline_calls_in_expr is guarded (enter_inline_expr_recursion, cap MAX_INLINE_EXPR_RECURSION_DEPTH = 128, added for #733), but inline_calls_in_stmts is not. A stmts→stmts recursion chain (nested blocks / closure bodies / re-walks of freshly inlined bodies) never passes the expr-side guard, so the shared depth budget never binds and the mutual recursion grows without limit on this bundle's AST shapes.
Fix being validated
Mirror the guard at the inline_calls_in_stmts entry, sharing the same thread-local depth budget: on cap, clear exact_receiver_facts and return with the subtree un-inlined (semantics-preserving — the inliner is an optimization pass; identical rationale to the existing expr-side bail). Validation against the pi bundle is running; PR to follow if green.
Repro: secret-tests/pi-target/build-bundle.sh output (shrinkwrap-pinned npm install + esbuild, recipe in tracker #6564) — deterministic input; happy to help minimize to a small fixture if wanted.
Also worth considering upstream (secondary): an env override for the perry-main stack size — the current 128MB constant has now been outgrown twice (v0.5.973 ioredis bump, this).
First wall of the pi coding-agent bring-up (tracker #6564): compiling pi's bundle (13.3MB unminified esbuild ESM output of
@earendil-works/pi-coding-agent@0.80.10) crashes the compiler:Reproduces identically at the stock 128MB
perry-mainstack AND at a 512MB test bump — so this is effectively unbounded recursion, not a merely-deep finite walk.Symbolicated crash stack (strip=none rebuild, macOS .ips + nm)
The overflow is the call inliner's mutual recursion, entered from module collection:
Root cause
inline_calls_in_expris guarded (enter_inline_expr_recursion, capMAX_INLINE_EXPR_RECURSION_DEPTH = 128, added for #733), butinline_calls_in_stmtsis not. A stmts→stmts recursion chain (nested blocks / closure bodies / re-walks of freshly inlined bodies) never passes the expr-side guard, so the shared depth budget never binds and the mutual recursion grows without limit on this bundle's AST shapes.Fix being validated
Mirror the guard at the
inline_calls_in_stmtsentry, sharing the same thread-local depth budget: on cap, clearexact_receiver_factsand return with the subtree un-inlined (semantics-preserving — the inliner is an optimization pass; identical rationale to the existing expr-side bail). Validation against the pi bundle is running; PR to follow if green.Repro:
secret-tests/pi-target/build-bundle.shoutput (shrinkwrap-pinned npm install + esbuild, recipe in tracker #6564) — deterministic input; happy to help minimize to a small fixture if wanted.Also worth considering upstream (secondary): an env override for the
perry-mainstack size — the current 128MB constant has now been outgrown twice (v0.5.973 ioredis bump, this).