wcc: arm asserttyped bail — a nil-typed value node is now fatal

The wwstage asserttyped pass only WARNED on a checked value-node with no
result type_, a check-bail-discipline regression that let gate-blind nil-stamp
miscompiles ship green (the whole #6 arc: tuple/struct/fn-ptr/enum/binding
nil-stamps were all invisible to the byte-id gates). With every nil-gap class
now stamped (module-qual calls, fn-ptr-field calls, computed enum value-exprs,
for-range/massign binds) the bail can finally arm: warn -> os.exit(1).

Exempt exactly the two legitimately-no-type value classes, each a positive
cited assertion (never a residual warn): the EXPR_ASSERT family (abort/assert,
guarded against a user shadow; harec check.c:877,893) and seeded pseudo-builtin
callees (len/append/free/alloc/size — a structural nil-decl-SK_FN predicate,
not a name-list). The pre-existing module-ref and dot-lhs filters stay: they
identify access-path components that aren't value exprs (harec EXPR_ACCESS),
not exemptions.

Verified clean over the broadest net — the armed checker over all five
self-build combined units (the full selfhost source) plus the 901 gap corpus —
zero out-of-class bail; fails-loud confirmed (undeclared call, abort's args, a
nil dot-base all bail). Checker-only: 990-997 byte-id hold.
This commit is contained in:
2026-05-28 10:40:08 +09:00
parent f91d93e827
commit 719893743e
4 changed files with 127 additions and 33 deletions

View File

@@ -4,25 +4,26 @@
* The live `make test` stdlib _run suite drives the CSTAGE `ww`
* (cmd/ check.c), which has no asserttyped pass; 990_selfhost feeds
* the ww-stage dumper only -t/-a (tokens/ast), never the checker. So
* the wwstage checker's asserttyped diagnostics (check.ww:3442 — the
* #15 / A.6.2.1e post-checker nil-type invariant gate, currently a
* soft warn) are UNOBSERVED by every other test. Re-arming asserttyped
* to bail would ship green while the known nil-stamp gaps stayed
* latent (the bootstrap-coverage trap).
* the wwstage checker's asserttyped diagnostics (check.ww — the
* #15 / A.6.2.1e post-checker nil-type invariant gate) are UNOBSERVED
* by every other test, yet the gate is now ARMED (a non-exempt
* nil-typed value node writes its diagnostic and os.exit(1)s). This
* probe is the visible counterpart: it counts the per-file diagnostics
* without gating on the bail's exit so a regression names its class.
*
* This probe runs the wwstage checker (wwdump_ww -c) over the
* gap-bearing combined.ww corpus, counts the per-file asserttyped
* diagnostics on stderr, and pins each count against the manifest
* below. A fresh nil-gap (count up) or a regressed fixed class (count
* up from 0) fails loud; a fold that closes a class drives its count
* down, which fails until the manifest is edited to match — i.e. the
* manifest is the expected-fail list that shrinks per fold and reaches
* all-zero exactly when the bail is safe to arm.
* down, which fails until the manifest is edited to match. Every class
* has reached all-zero, so the manifest is now the all-closed floor
* that holds the armed bail green.
*
* Gap classes (counts verified empirically at this revision):
* A module-qual N_DOT call result checked_test 0 (closed)
* B fn-ptr struct-field call smoke 0 (closed)
* C abort intrinsic callee utf8 8
* C abort intrinsic callee utf8 0 (exempt)
* D module-leaf == type/fn name fnmatch 0 (closed)
* D module-leaf == type/fn name random 0 (closed)
* E computed enum-member value-expr enum_corpus 0 (closed)
@@ -114,7 +115,7 @@ main(void)
{ "selfhost/test/smoke.combined.ww",
"B fn-ptr struct-field call", 0 },
{ "lib/encoding/utf8/utf8.combined.ww",
"C abort intrinsic callee", 8 },
"C abort intrinsic callee", 0 },
{ "lib/fnmatch/fnmatchtest.combined.ww",
"D module-leaf == type/fn name", 0 },
{ "lib/math/random/random_test.combined.ww",
@@ -159,7 +160,7 @@ main(void)
fail, n);
return 1;
}
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
"on %d gap-corpus fixtures (C pinned, A+B+D+E+F+G closed)\n", n);
printf("asserttyped_gap: ww-stage checker diagnostic set matches manifest "
"on %d gap-corpus fixtures (A-G all closed; bail armed)\n", n);
return 0;
}