Term depth
A term’s depth is not bounded by its syntax, and the language puts no limit
on it. Every walk that recurses over a term, parse to typecheck and the
printers, runs on the compile stack: compile::on_compile_stack, a thread
with a 64 MiB reservation, through which compile_and_typecheck and the
static halts run. A new recursive pass over the AST or the IR runs under that
door, or it is a stack overflow waiting for a long enough program.
- Why syntax does not bound it. The parser’s
NESTING_DEPTH_LIMIT = 64caps delimiter and prefix nesting, which is the parser’s own recursion. A sum$[1+…+1], a curried lambda{|x1 … xN| …}, a?chain, anelsifchain and a block’s statement list each nest one level per element in the AST or the IR: the Pratt loop builds a left spine,parse_blockcurries,Chainlowers to right-nestedtry,elsiffolds, and a block body folds into aBindchain. - Why not a measure. A block is a
Bindchain, walked by the checker, the annotator and the drop glue alike, so no depth measure rejects a deep term without rejecting a long block; and a limit safe on Windows’ 1 MB main stack would reject a few hundred statements. - Why not
stacker. Tried and declined: it protects only the walks that are wrapped, while derivedDrop,Clone,Debugand the type walks (apply_ty,free_ty,generalize) stay recursive, and a recursion inside a grown segment gets that segment’s 1 MB, less than it had before. One thread, one reservation, one site. - What is still recursive, and where. The parser, bounded by its cap. The
evaluator is an explicit-step machine and recurses over nothing. The checked
Toplevelis dropped on the caller’s thread, where drop glue at about 200 bytes a level handles 40 000 levels in 8 MB and about 5 000 in Windows’ 1 MB main stack: the one edge the door does not cover. - Measured. Release binary, 8 MB stack, before the door: 5 000 parameters,
?arms orelsifbranches overflowed the checker; a 5 000-statement block or 5 000-term sum survived and 20 000 did not. A debug build spends about 13 KB of stack a level in the elaborator.core/tests/deep_terms.rsholds the five shapes at 2 000 on the 2 MB test thread. - Unification alone is budgeted. A depth budget belongs to unification,
not to every traversal of a type:
MAX_UNIFY_DEPTH = 512(unify.rs), charged bydeeper()on each descent into a strictly deeper subterm, since a substitution composes types the program never wrote beside one another.apply_ty,free_tyandgeneralizecarry none, androw_occursspends the caller’s budget. - Where.
compile::on_compile_stackandCOMPILE_STACK(core/src/compile.rs);ral/src/batch.rs::haltedfor--dump-astand--dump-ir;syntax::NESTING_DEPTH_LIMIT;typecheck::unify::MAX_UNIFY_DEPTH.