The wcc test drivers ran `ww build <bare-/tmp src>` with no -o, so the
compiler's <stem>.sepwork scratch landed beside the source and was never
cleaned: unbounded /tmp growth (2195 stale dirs observed) that fills tmpfs
and fabricates phantom test failures + silent harness aborts, and for
in-repo fixture builds leaked .sepwork into the tracked tree.
Each leaking build now writes its source + output inside a per-invocation
tmpdir, passes -o <tmpdir>/<stem> so the .sepwork lands inside it, and
rm -rf's the tmpdir on every exit path -- including fopen-fail and the
expected-fail reject builds (scratch is mkdir'd before the build can fail).
`ww run` and explicit-`-o`/byte-id helpers are left as-is; the 990/993
byte-id comparison logic is byte-for-byte unchanged.
Two items filed separately (this commit holds the no-Makefile / no-main.c
rail):
- #13: a stale <src>.s byte-id readback (749) silently no-ops since
separate-compile emits .s to <ostem>.sepwork/__root.s; documented inline.
- #14: build-system Makefile recipes build selfhost/cmd/*/main.ww with no
-o and leak main.sepwork in-tree (bounded, gitignored; own commit).
One concern -- sepwork leak hygiene -- across 228 drivers; uniform
transform applied per-file and two-round reviewed. make test: all 402
passed, zero net-new /tmp scratch, zero test-driven in-repo .sepwork.
The reviewer flagged two gaps in the #7 coverage: no minimal
non-empty count (a 1-element [_] is the boundary the element-counter
must still get right) and no module-level array element read-back
(only .len was checked at module scope). Add local_one_len /
mod_one_len / mod_one_elem. Both still mutation-resistant: the old
collapse-to-0 reads .len as 0, not 1, so the new len rows fail it too.
42/42 dual-stage + byte-id.
`[_]T = [...]` (canonical Hare array-length inference) silently
miscompiled to a zero-length array: the parser already left the array
type's length child nil as the infer sentinel — distinct from an
explicit [N] — but neither checker stamped the real count, so `len(x)`
returned 0 with no diagnostic (rule-7 silent miscompile). Module-level
was worse on wwstage, where `x.len` on ANY global array (even an
explicit [N]) fell to the SB fallback and mis-emitted `MOVQ len(SB), AX`
(linker: undefined reference to len).
The length lives in the stamped TYPE and cgen already keys stride /
length / data-emission off it, so stamping the inferred count at the one
checker inference point closes it permanently (rob's #7 ruling):
- check.c clet + module-level N_LET pass-2: count the initializer's
elements and patch the array type's length (the Sym too, so a later
x.len reads the inferred alen). No-init / non-array init can't infer
-> loud error, never a silent zero-length array.
- check.ww inferarraylen: the wwstage twin — stamp a synthesized
N_INTLIT length child before resolvewalk caches the array tinfo;
same loud-error rule. Idempotent for the module-level double-call.
- cgenexpr.ww cgdot: the missing wwstage arm for a top-level [N]T
global's .len / .ptr (cstage cgen.c:8011 already had it).
- cgenutil.ww letslotsize: drop the now-redundant [_] slot-size
intercept — a workaround for this very bug; the stamped length flows
through the general slotsize path (rule 7).
Both stages converge byte-identical; new table-driven test 684 covers
[_]int/[_]str/[_]u8 local + module-level, len + element read-back,
dual-stage runtime + asm byte-id, plus three negative no-infer rows.