Reject helpers checked only rc!=0, so a SEGFAULT (exit 139, or the
driver's "w6c failed" exit 1) counted as a clean reject -- a wwstage
crash could pass vacuously (it did, latently, on bodied bare-... pre
#11). Every reject row now captures stderr and requires the actual
diagnostic substring (rc!=0 AND strstr) across all 16 reject tests; a
crash emits no diagnostic, so it now fails. This is the
differential-reject backstop (#15): both stages must cleanly reject
with the expected message.
Two genuine cstage/wwstage diagnostic-body divergences are documented
inline via per-stage substrings, not papered over (845 tuple parse,
catA_f2 tuple arity); catalogued in #21.
Wwstage w6c_ww silently exited 0 on parse errors (stderr noise
only). The driver ran cgen on the broken AST then only checked
l.errs; ps.errs was never read, so callers downstream
(`ww build`, make rules) saw no signal and proceeded with junk
asm. Cstage cmd/w6c/main.c gates on `l.errs || p.errs` before
cgen; mirror that, hoisting the check above cgfile so the broken
AST never reaches codegen.
New test 742_parse_error pins the contract on both binaries:
writes a known-bad fragment to a pid-scoped /tmp file, runs cstage
w6c unconditionally and wwstage w6c_ww if available, asserts both
exit non-zero. Pre-fix wwstage exited 0 with junk asm; post-fix
exits 1 with parse: messages preserved on stderr.
116/116 ok. ww2 == ww3 == ww4 byte-id holds (no behavior change
for valid input).