TYCHO Search

Search documentation

    ↑ ↓ to move · Enter to open · Esc to close

    What the next program says the language needs

    Status: DONE. Program: tycho-scheme — Scheme interpreter in tools/tycho-scheme/, first program to use upward closures and recursive value trees at scale. Outcome: 2 phases complete 2026-08-03 — runtime stack guard shipped, 2 stale docs corrected.

    This plan is a fresh clone, 2026-08-03: the completed tycho-vm plan lives at docs/internals/plan-tycho-vm-DONE.md. The rule from that plan holds here: *does anything that is not the program written to want it need this?* A finding becomes a phase only when a second, independent caller exists.

    The program

    tycho-scheme -- a Scheme interpreter in tools/tycho-scheme/, the first program in the tree to use upward closures (returning a function that captures), recursive value trees at interpreter scale, and an environment pool (the flat-index idiom the thesis recommends). Reader, evaluator, printer, and a small standard library all in Tycho, gated the tycho-vm way (compile programs, lock goldens byte-identical).

    Findings recorded while probing the design, before a line of the interpreter:

    Phases below appear as the interpreter finds friction; a finding is a phase only when a second, independent caller needs it.

    Findings, while writing it

    Phases

    Phase 1 -- generated-code recursion must fail closed [DONE 2026-08-03]

    Why then: the recursion finding gained its second caller. The compiler fails closed on pathological input (tests/recursion/run.sh); generated code had nothing -- deep recursion in emitted C overflows the C stack and dies with SIGSEGV (exit 139), no diagnostic, no cleanup. Two measured callers: the interpreter at ~5k Scheme levels ((sum (range 0 5000))), and the json walker at ~100k nests (10,000 ok / 100,000 SIGSEGV, verified 2026-08-03).

    Mechanism -- chosen by measurement. The runtime stack guard (option 1) won:

    Cost of covering spawned tasks: +7% on spawn-bound workloads. A spawned task runs on its own thread with its own stack, so the trampoline (tycho_task_start) installs per-thread bounds AND an alternate stack before the task's code runs. Measured 2000 spawn+wait of a trivial fn: 38.7/40.2/40.8 ms before vs 42.2/42.6/43.2 ms after (~1.4 us/spawn: pthread_getattr_np + one sigaltstack syscall). Everything else (fib, sort, non-spawning programs) shows no movement. The trampoline's start struct is bump-allocated in the task's own root arena, not malloc'd.

    Two finds during implementation, both fixed:

    Where it lives: runtime/tycho_rt.c -- the constructor, the handler, and tycho_task_trampoline. The runtime is embedded verbatim into every generated C file, so no compiler change was needed; _GNU_SOURCE was added to the runtime's feature-macro block (pthread_getattr_np and the REG_* ucontext indices are __USE_GNU-gated; the 13 corelib shims define no GNU-conflicting names, checked before widening).

    Tests: tests/recursion/run.sh grew a generated-code side: three deep programs (big-frame n + f(n-1), small-frame tail f(n-1), and a SPAWNED task's own stack) must compile, then die cleanly -- exit 1-127, empty stdout, stack overflow on stderr -- plus the modestly-nested counterparts that must run and print the right answer. Inputs are generated in the runner, so no goldens moved.

    Gates, all green: make test 582/0, make ilp32 582/0, make conc 38/0, asan-self 599/0, make corelib green and shim-check 12/0, the four tool lanes (vm/ar/q/scheme) green, and the extended recursion runner green.

    What the ci sweep caught (both real, both fixed):

    Phase 2 -- correct the two stale docs proven wrong by the program [DONE 2026-08-03]

    Both were recorded as findings while writing the interpreter; both proven false on this tree, each a one-line correction:

    1. docs/architecture.md:52 -- "closures (downward value-capture)" is wrong: tools/tycho-scheme/progs/closures.scm returns closures that capture and two counters from one factory stay independent (1 2 1). The spec's capture-by-deep-copy (§09-expressions.md) is the truth. Gate: the two doc gates only (check_citations.py, check_links.sh).
    2. corelib/json/json.ty:15-17 -- "Objects are parallel key/value arrays because tycho's maps don't take a recursive value type here" is wrong: a recursive enum as a map value compiles and runs ([]string: Tree with TNode(Tree, Tree), probed 2026-08-03). The parallel-array design itself stays. Gate: make corelib plus the two doc gates.