Skip to main content

sema_core/
stack.rs

1//! Stack-growth guard for deeply recursive work.
2//!
3//! Recursive value walks (`Display`, `value_to_json`, `pretty_print`) and
4//! re-entrant native→VM calls consume the real OS thread stack per level. On a
5//! deep-but-finite structure — or a recursive Sema function that maps over its
6//! children — this overflows the fixed 8 MB main-thread stack and aborts the
7//! process with an uncatchable SIGABRT, *before* the VM's frame guard can turn
8//! it into a catchable error. [`maybe_grow`] grows the stack on demand at those
9//! recursion points so the process survives to hit the guarded limit instead.
10
11/// Grow when fewer than this many bytes of stack remain.
12const RED_ZONE: usize = 128 * 1024;
13/// Size of each freshly allocated stack segment.
14const STACK_SIZE: usize = 4 * 1024 * 1024;
15
16/// Run `f`, first extending the stack if it is near exhaustion. Cheap when
17/// there's ample stack left (a bounds check), so it's safe to call at every
18/// level of a recursion.
19#[cfg(not(target_arch = "wasm32"))]
20pub fn maybe_grow<R>(f: impl FnOnce() -> R) -> R {
21    stacker::maybe_grow(RED_ZONE, STACK_SIZE, f)
22}
23
24/// wasm cannot grow its stack, so this is a plain call (deep recursion there is
25/// bounded by the same limits as before).
26#[cfg(target_arch = "wasm32")]
27pub fn maybe_grow<R>(f: impl FnOnce() -> R) -> R {
28    f()
29}