Follow-up from #75.
The interpreter's runtime recursion guard MAX_RECURSION_DEPTH = 512 (packages/interpreter/src/interpreter.zig, evaluateExpression) does not actually protect against native stack exhaustion, and is unreachable for the case it looks designed for:
Pure expression nesting can't reach it — the parser rejects nesting deeper than 256 first (
EP0001"Expression is too deeply nested (maximum depth is 256)"). So a deeply-nested expression never reaches the runtime guard at all.Function self-recursion crashes before it — a genuinely self-recursing Home function (
fn rec(n) { return rec(n+1) }) exhausts the native stack (SIGSEGV) before logical depth 512, because each Home-level call chains several large native frames (callUserFunctionWithNamed→executeStatement→evaluateExpression→evaluateCallExpression→ …). Observed as a hard crash in a test thread (smaller stack); the main-thread CLI may tolerate more but the guard still doesn't bound it deterministically.
Net: the three limits (parser 256, runtime 512, actual native-stack budget) are not reconciled, so the runtime guard provides little real protection.
Possible directions
- Lower
MAX_RECURSION_DEPTHto a value that trips before native overflow for the function-call chain (measure per-level native stack cost), or - Add an explicit stack-depth/remaining-guard check in
callUserFunctionWithNamed(the deep frame), not only inevaluateExpression, or - Align the parser cap and runtime cap intentionally and document the relationship.
Not blocking anything today — surfaced while fixing #75 (uninitialized recursion_depth).