Summary
home run <file.home> — the Home-language tree-walking interpreter — cannot run hello world. Any program that evaluates a literal in value position (a literal passed directly to print, a literal return, or a top-level let x = <literal>) aborts with:
stack overflow: expression recursion depth exceeded 512
error: RuntimeErrorexamples/hello.home (fn main() { print("Hello, Home!") }) fails this way, as do examples/simple_math.home and most example programs.
Repro matrix
Built zig build debug → zig-out/bin/home-debug (also repros on home-release-safe).
| Program | Result |
|---|---|
fn main() {} | ✅ Success |
fn main() { let x = 5 print(x) } | ✅ Success — literal bound, printed via variable |
fn main() { let a = "x" let b = a print(b) } | ✅ Success |
fn main() { print("hi") } | ❌ recursion-depth 512 |
fn main() { print(42) } | ❌ recursion-depth 512 |
fn main() { let x = "hi" } | ❌ recursion-depth 512 |
fn main(): string { return "hi" } | ❌ recursion-depth 512 |
The discriminator is a literal evaluated as a value that is consumed directly (call argument, return operand, terminal let initializer). Passing the same value through a variable binding first works, and empty bodies work.
Mechanism / where to look
- Guard/counter:
packages/interpreter/src/interpreter.zig:1086-1092(evaluateExpression,recursion_depth += 1; defer recursion_depth -= 1; if (> MAX_RECURSION_DEPTH=512) …). The counter itself is balanced (increment +deferdecrement), so the 512 trip is a genuine non-terminating re-dispatch, not a leaked counter. - The literal arms are trivial and do not self-recurse:
.IntegerLiteral—interpreter.zig:1094.StringLiteral—interpreter.zig:1142(processStringEscapesthen return)
- So the infinite recursion is in the consumer path that re-feeds a literal back into
evaluateExpressionwithout progress. Candidate dispatch sites:evaluateCallExpression—interpreter.zig:2550, builtinprintatinterpreter.zig:3049/interpreter.zig:11132- the
return-statement and terminal-letevaluation paths
A likely shape: an argument/return-value coercion or "evaluate then re-evaluate to coerce" step that calls evaluateExpression on the same *ast.Expr again when the value is a bare literal (but is short-circuited when the operand is already a resolved variable value).
Impact
- Home cannot run
print("…")— i.e. hello world — viahome run. Every example that prints a literal is broken. - Blocks any interpreter-level perf work (can't benchmark a path that can't evaluate a literal). Found while doing perf recon on the interpreter.
Notes
- The native-compile path (
home build) is not covered by this report — this is specifically thehome runinterpreter. - Recent parser commits (
c73c0bfcdfn forward decls,eba11d36cwhile-continue,9d8b7c14eerror-union sugar) touched AST shape; worth checking whether one of them altered literal-node handling and regressed this, vs. a long-standing latent bug.