Commit Graph

832 Commits

Author SHA1 Message Date
abd97e6c57 lib/getopt: printusage + printhelp
Port ref/hare/getopt/getopts.ha:202-314 (printusage / _printusage / containsh /
printhelp): the two-pass io.empty width measurement + the >72-col indent rule.
Table-driven tests. cstage-only (C-first); wwstage byte-id twin owed (#125).
printsubcmds + parse() deferred (#127 / #126).
2026-06-07 06:19:13 +09:00
f3750ae3ce lib/io: empty() stream; wcc/cgen: #129 sretretsize + #130 global tagged-field store
io.empty (discard+EOF stream, ref/hare/io/empty.ha:4-17) — needed by getopt's
two-pass printusage width measurement. Diverges from Hare's `const empty: *stream`:
a `let _empty_vt` + `fn empty()` that wires the fn-ptr slots per call, because
const-init of a vtable struct with fn-ptr fields is blocked (#118, ruled accept).

Co-discovered while making empty() byte-identical across stages: three
wwstage-only cgen fixes (cstage was already correct; wwstage aligned down):
- #129 sretretsize: consult the same-module pointer-alias before structlookup's
  any-module struct fallback (io.stream = *vtable was mis-sized as memio's 56B
  struct -> spurious sret save).
- #129 callsretsize: swap curmod to the callee's module before sret-size
  classification (cross-module callee context).
- #130 cgassign global-struct tagged-union field store: add the missing arm
  (was a 1-word store) mirroring cstage cgen.c:4893-4912.

The three are inseparable from io.empty here — splitting them out leaves a
divergent-asm intermediate (993/995 red), so they ride one commit per the
one-class gate-repair carve-out (#133-expanded precedent). Regenerates the
embedded combined.ww; 989_lib_byteid pins bufio + fmt graduated to M_ID.
(cgenexpr.ww fix-3 inline comment cites the #129 cluster; narrow to #130 on
next touch to avoid a regen for a comment.)
2026-06-07 06:19:13 +09:00
3ae24c22b8 lib/regex: #123 POSIX character classes [:class:] — charclass_map + compile/exec arms
12-entry charclass_map (str, *fn(rune) bool) + bracket [:class:] recognition
(compile) + charset_class_item predicate dispatch (exec), porting
ref/hare/regex/regex.ha:74-87/190-204/726-733. Table-driven tests cover all 12
classes (±), negation, composition, and the "No character class after '[:'"
error path.

cstage-only (C-first per the speed pivot); wwstage byte-id twin owed in the
batch-converge phase.
2026-06-07 03:10:55 +09:00
66d69537a5 wcc/cgen: #124 cross-module &fn in a const — N_DOT reloc + checker accept (both-stage)
A cross-module `&module.fn` in a const emitted no static reloc (the
const was never defined -> w6l undefined-reference, both stages) and
wwstage's checker rejected the const fn-table. #117/#119 wired the
&fn->DATAR const-data reloc for SAME-module &fn only; charclass_map
(fold-6) needs cross-module (12x &ascii.isXXX).

cgen: add the N_DOT arm to the &fn->symbol helper (node_fnptr_sym /
nodefnptr + the two ww emit sites), emitting mafn(leaf, module-ident)
-- exactly the symbol a runtime &mod.fn or a direct cross-module call
already emits. The helper is the SSoT for both the scalar (#119) and
tuple-row (#117) const-data paths, so one arm closes both.

checker: type a cross-module `&mod.fn` as `*fn(...)` in the TK_AMP arm
(the N_DOT twin of #206's N_IDENT fn-ptr synthesis, gated on a resolved
SK_FN/N_FNDECL leaf), so isassignable affirmatively accepts the const
table -- aligning wwstage UP to cstage's actual acceptance reason
rather than by abdication. The SK_FN gate keeps a non-fn `&mod.var`
from synthesizing a fn type (the one pre-existing nonfn-scalar cs!=ww
slip is N_IDENT-base, untouched and reproduces same-module).

One consumer-coupled commit (the checker accept gates wwstage cgen, so
neither half is independently testable). Narrow: slice-row + scalar
only; fixed-array (#118) and struct-field (#129) stay separate. Both
stages emit the correct cross-module symbols at the right tuple-slot
offsets -> byte-identical (990-997 green). Pin 949_xmod_fnptr_const_run
(distinct fns so a wrong reloc is caught + the SK_FN-gate axis). This
was the last fold-6 cgen blocker; charclass_map is now unblocked.
2026-06-06 23:34:46 +09:00
754944a755 wcc/cgen: #121 indexed tuple-element read + literal-store round-trip (both-stage)
Reading or storing a tuple element of an indexed array element was
broken across the board (the fold-6 read-path). One fused commit,
both stages, four faces of indexed tuple-element access:

 - FIELD read `tbl[i].N`: was loud ("unsupported field-read shape" --
   the field-read dispatch keyed on an N_IDENT base; an INDEX base fell
   to a fatal). Now resolves &tbl[i] via the place-spine and reads the
   field at addr+foff through the existing per-kind arms (str-triple /
   scalar / fn-ptr).
 - WHOLE read `let e = tbl[i]`: was a silent word0-only truncation
   (plain-tuple kin of #37/#58, which covered only tagged). Now a full
   cursor fill from &tbl[i].
 - STORE `a[i] = (3,4)` (N_TUPLE-literal rhs): was a silent word0-only
   store -- the write face of the read. The aggregate-store-into-index
   site handled ident/dot/deref tuple rhs but not the literal; now it
   materializes the literal and word-copies. Narrow: N_IDENT base only
   (N_DOT/chained stay deferred, #270).
 - for-range over a const-slice-of-tuple: was a divergent SEGV; now a
   symmetric loud-stop on both stages (filed #122).

The store and read were a round-trip that passed test 809 only by luck
(broken store XOR broken read canceled). Fixing the read alone exposed
the silent store; rule-7 obliges fixing both, so 809 is now genuinely
correct, not luck-correct. Both faces are byte-id-blind (#263) -- the
net is a runtime round-trip pin with distinct-per-word values and a
real call clobbering the cursor registers between store and read, so a
word0-only store or read is caught. Both stages byte-identical
(990-997 green). Pin 947_tuple_index_read_run.
2026-06-06 22:23:43 +09:00
761cfa4524 wcc/cgen: #117 pin scalar-tuple-slice row (broad-arm in-suite net)
rob A-corrected: the broad TY_TUPLE arm stays; pin an accepted non-(str,*fn)
scalar-tuple slice in-suite. const [](i64,str) with TYPED 1i64 (untyped int
is ww-checker-rejected, #120); scalar-first so the i64 reads back via the
word0-correct whole-element cursor (#121: .1/word3 truncated). Distinct
values + len(t) + cs==ww byte-id lock the scalar-in-slice-backing path.
Base f8be2ae has no TY_TUPLE arm -> louds. Test-only; 17/17 ok.
2026-06-06 21:13:32 +09:00
28396f012b wcc/cgen: #117 review — cite #120 at cstage tuple arm (cs-accept/ww-reject divergence)
The #117 emit_slice_data TY_TUPLE arm fires for any foldable tuple row,
but wwstage's checker admits only the (str,*fn) shape and loud-rejects
the rest before cgen. Document the retained acceptance divergence at the
site (rule 7/8) with the #120 pointer. Comment-only, asm-neutral.
2026-06-06 20:54:39 +09:00
1b4f25ac45 wcc/cgen: #119 scalar &fn global DATA via the #117 reloc helper (both-stage) 2026-06-06 20:28:50 +09:00
942abf0482 wcc/cgen: #117 const slice-of-(str,*fn) DATA + &fn->DATAR reloc (both-stage) 2026-06-06 20:27:43 +09:00
df1928182e wcc/cgen: #117 prep — factor emit_tuple_row backing-relative (byte-neutral) 2026-06-06 20:02:32 +09:00
f8be2ae8dd wcc/cgen: #116 non-literal tuple source into a tagged box (both-stage)
cg_widen_tagged_store only handled a tuple LITERAL (N_TUPLE / cast-of-
N_TUPLE) widened into a tagged box; any addressable non-literal tuple
source -- IDENT var, INDEX tbl[i], DEREF *p -- hit the `else fatal`
("tuple-typed source shape unwired"). Both stages loud-identical
(honest, no silent miscompile). This blocked indexing a const tuple
table into a union (regex charclass_map[i] -> charset union).

Add an addressable-tuple-source arm, both stages (cgen.c +
cgenutil.ww twin). It resolves the source address via the cgplaceaddr
place-spine (covering ident/index/deref -- one mechanism, so the trio
is family-closed) and block-copies the tuple's type-table ->size bytes
into the box payload (after the 8B tag), then stamps the variant tag.
No re-slotting: a tuple's in-memory layout uses the same eslot strides
(str=24B header, *fn=8B, ...) as the box payload the literal loop
fills, so source-layout == dest-layout. The existing narrow-pack and
tag-unresolved guards stay as the honest boundary; CALL/sret tuple
sources (different receive, #68-kin) stay loud.

align-BOTH: both stages were loud (no runtime reference), and byte-id
is structurally blind to an identical-wrong emission -- so correctness
is proven by a RUNTIME read-back pin (944_nonlit_tuple_widen_run, per
shape: match-extract + assert str header + call the fn-ptr elem with
distinct fns so a stale pointer is caught). 936's old reject row
graduates to a run row. Both stages byte-identical (990-997 green).
2026-06-06 19:27:28 +09:00
da30f10f70 wcc_ww/check: #47 gap-B tuple-with-tagged case-arm variant-match (align-up)
wwstage's checker rejected a `case let t: ((void|size),(void|size),
size) =>` arm against a (tuple|error) scrutinee ("case: not a variant
of scrutinee"), while cstage accepts and runs it. typeeqast's N_TTUPLE
arm recurses per-element, but a tagged element (void|size) is
N_TTAGGED -> fell to the conservative catch-all `return false`, so the
whole tuple-compare failed. typeeqast is the sole acceptance route
(casevariantpairmatch is N_TNAME-only).

Add an N_TTAGGED arm to typeeqast, sibling of N_TTUPLE, mirroring
cstage type.c:288-300 (type_eq TY_TAGGED): position-by-position
variant compare over the tagged node's .list (direct nodes, not
.lhs-wrapped). cstage's nullable-flag check is deliberately not ported
(resolved-Type property, no ww AST analogue; moot for case-match).

A spread variant (TK_ELLIPSIS) in the .list is loud-rejected rather
than compared: a naive streq would silently accept a `...ab` case that
cstage rejects (a new cs!=ww over-accept the bare arm introduced).
Flattening the spread is deferred (#115); until then it louds, matching
cstage.

ww-only (cstage already accepts); the gap-A cgen store landed in
6a5bb3e. wwstage now accepts the b1c match and runs the full shape
byte-identical to cstage -> #47 (both gaps) closed. The deferred
full-b1c row in 944_tuple_tagged_union_run is promoted to a both-stage
runtime row. Checker change is acceptance-only/additive -> bootstrap
byte-id neutral (990-997 green).
2026-06-06 17:36:10 +09:00
6a5bb3efc9 wcc/cgen: #47 gap-A tuple-in-union tagged-element store (both-stage)
A tuple containing a tagged-union element, used as a union member
(e.g. ((void|size),(void|size),size) | error), loud-stopped in the
cgen return-store: the tuple-in-union store walk had scalar/float/
str/slice element arms but no TY_TAGGED-element arm. A PLAIN
tuple-in-union already worked -- the blocker was the tagged element.

Add the recursive two-level widen arm at both stages
(cg_widen_tagged_store / cgwidentaggedstorebp): for each tagged
element, re-enter the tagged-box store (inner tag@slot+0,
payload@slot+8) at the element's tuple-payload offset, then stamp the
outer tuple tag. Slot strides come from the type table
(roundup8(eu->size)) -- the checker already sizes the shape correctly
(tuple->size measured 40, union box 48; check.c:715-720). The
recursion descends a finite type tree (a tagged element is never a
tuple literal, so it can't re-enter the tuple arm); unsupported
deeper nesting still louds via the existing size/tag guards.

Both stages get the same arm -> byte-id (990-997 green; additive,
bootstrap-neutral). cstage runs the full b1c shape
(construct+return+match-extract) as the runtime reference; wwstage's
store rides on byte-id until gap-B. gap-B (wwstage checker
match-acceptance of the tuple-with-tagged case pattern) is a separate
commit -- wwstage still louds the match honestly at the checker.
Pin 944_tuple_tagged_union_run.
2026-06-06 16:57:42 +09:00
351abb0ab3 wcc/cgen: #58 indexed tagged-field read+assign cursor arm (both-stage)
Reading or writing a tagged field of an indexed array element
(xs[i].field) was broken on BOTH stages, byte-identically and
silently (#263 gate-blind): the arr[i].field branches had arms for
array/str/slice/float but no TY_TAGGED arm, so the tagged field fell
to the single-word scalar path. READ loaded only the tag word (stale
payload -> `xs[i].min as T` read garbage); ASSIGN stored the raw
unboxed scalar into the tag slot, corrupting the box.

Insert a TY_TAGGED cursor arm before each scalar fallback, both
sites both stages (cgen.c read + assign; cgenexpr.ww cgdot N_INDEX-lhs
read + cgassign indexed-field). READ mirrors cg_tagged_memread
(payload -> DX/CX/R8, tag -> AX last). ASSIGN synthesizes the tag for
the concrete variant (taggedvariantindext) and stores tag+payload via
the str/slice 3-word store spine -- not the source-remap widener
(concrete rhs has no source tag to remap).

>32B / multi-word / float payloads are loud-stopped at all four arms
(emission not yet wired; see #114). That shape is reachable today via
a narrow-variant ctor, so it louds rather than silently miscompiling.
Both stages get the same arm -> byte-id preserved (990-997 green; the
runtime is the net for this #263 class). Pin 944_idx_tagged_field_run
(read/assign runtime rows + >32B expect-loud rows).
2026-06-06 16:21:45 +09:00
cc896bd078 wcc_ww/cgen: #55 tagged-source arg-widen into wider tagged slot (align-up)
wwstage pushargsrev treated a narrower tagged-union argument widened into
a wider tagged param slot as a concrete variant: taggedvariantindex<0
clamped the tag to 0 and pushed word0 only (deref/index/dot silent-wrong;
ident ran correct only by prefix-union tag-index luck). cstage is correct
(cg_widen_tagged_push routes src_is_tagged unconditionally); align ww UP.

Three arms in cgenutil.ww, all mirroring cstage cgen.c:
 - slot-gate the ident aistagged short-circuit so a slot-differ tagged
   ident falls to the widen path instead of the raw 2-word push;
 - route a tagged source in the widensz>0 arm through @tagscr +
   cgwidentaggedstore + push high->low (cgen.c cg_widen_tagged_push);
 - cgwidentaggedstore cursor arm (<=32B INDEX/DOT) spills by source
   width, zero-pads, and tag-remaps (cgen.c 2698-2714) — was dst-slot
   spill of stale high regs with no pad and no remap.

Same-slot tagged->tagged is byte-id-neutral by construction (empty pad +
identity remap). cstage untouched; 4 legs x {aligned, misaligned-tag}
converge ww->cs byte-identical. Pin 944_tagged_widen_arg_run.
2026-06-06 15:15:13 +09:00
00d9580c9f wcc/cgen: #84 uninit [N]T array zero-fill (both-stage)
Drop the `!TY_ARRAY` exclusion in the bare-let no-rhs zero-fill (cgen.c
N_LET else + cgenstmt.ww cglet, both gated `sz>8 && !TY_ARRAY`) so an
uninit `[N]T` array local zero-fills like every other composite (Go-zero
per user ruling). The zero-fill extent is the array's chased ABI size
(lu->size / chased tinfo.size, rule-13 — never a hardcoded count*esz),
NOT the slot-padded letslotsize, so a non-8-multiple array ([20]u8 = 20)
zeroes its exact bytes instead of over-zeroing to the 24B slot. The
unrolled MOVQ/MOVL/MOVB run mirrors the existing composite path; the
largest real local array ([256]u8) is 32 MOVQs (pathbuf[4096] is a
module GLOBAL, BSS-filled — never on this stack path, so no large-fill
case exists).

Closes a gate-blind #263-class bug: `let a: [3]int;` (no init) read
whatever the stack held — a clean frame masked it (fresh stack = 0), a
dirtied frame exposed it (d_array=165 garbage). BOTH stages emitted no
fill, both-wrong-IDENTICAL, so the cs==ww byte-id net could not see it.
The load-bearing net is therefore a RUNTIME dirtied-stack zero-read
(944_array_zeroinit_run: array-elem / narrow [4]u32 / non-8-mult [20]u8
/ 2D + an initialized control), not asm presence.

Deliberate byte-id EVENT: every uninit-array source site gains zero-fill
insns, so the 990-997 .s MOVE vs the prior tree; cs==ww HOLDS (both add
the identical insns). The 990-997 byte-id + 995 self-rebuild staying
GREEN is the fixpoint proof — it proves every uninit compiler-array is
write-before-read, so the zero-fill is purely additive and the
ww1->ww2->ww3 self-rebuild fixpoint holds by construction. w6c/wwdump
main.combined.ww regenerated (cgenstmt.ww embeds there).

#84 is ARRAY-ONLY; the no-default reject-set (uninit tagged / plain-*T)
is split to #113, parked behind a ruling — selfhost relies on the
current (void|T) zero-fill (the "not-set-yet" idiom).
2026-06-06 14:10:17 +09:00
5d596206c6 wcc/cgen: #94 def-array indexed &-base leg (both-stage)
`&D[i]` over a module-level DEF array SEGV'd on BOTH stages: the
TK_AMP N_INDEX N_IDENT base classify checked only the local and let
legs, so a def-array base fell to a wrong else — cstage zero-based
the addend (XORQ BX,BX -> wild pointer, cgen.c) while wwstage
value-loaded the symbol (MOVQ name(SB) = D[0], not its address,
cgenexpr.ww complex-base fallback). Divergent asm, both wild.

Add one def-array leg per stage, mirroring the working let leg:
- cs: `def_isarraydef(base) -> LEAQ name(SB),BX` alongside let_islet.
- ww: the `defvartnode` fallback the read-side cgindex already takes
  (cgenexpr.ww:1762) -> N_TARRAY classifies isglobalarr -> LEAQ
  name(SB).
The def DATA symbol already exists (plain &D + D[i]-read work), so
once the base is the address the existing i*esz scale + ADDQ
round-trips. cs and ww now emit BYTE-IDENTICAL LEAQ-SB asm — the
both-broken -> both-correct convergence is the point (#263 class).

Rows (944_def_amp_idx_run, all 0/0 byte-id): amp_int [3]int,
amp_u32 [3]u32 esz=4 (narrow scale), amp_arg &D[2] as a func-arg;
controls ctrl_plain (&D), ctrl_read (D[i]), ctrl_2d (&M[1][1]) keep
working. *p spelled `let v: T = *p` — `*p: T` parses as `*(p: T)`.

OUT (filed #112): &D[..] slicing a def-array is a distinct parse
reject needing a Hare-fidelity ruling — not this leg.
2026-06-06 13:27:24 +09:00
26ba1ad1b5 wcc_ww/cgen+peellint: #109 close #101 primsize-alias family by construction
Route the 16 routable bare-primsize GUARD sites (is-primitive /
struct-vs-prim dispatch) through the #101 aliasprimsize SSoT helper.
Byte-NEUTRAL by construction: an alias-narrow name is already
neutralized downstream by the same arm, so routing emits no new asm
(the empty-flip-set ken oracled).
  Shape-A exclude-prim-early (3): cgenutil sretretsize / structparamsize
    / structfloatclass — `primsize>0 return` then structlookup→nil
    returns the same value; route returns it early, same.
  Shape-B prim-guard-then-structlookup (13): cgenutil 4604/4650 +
    cgenexpr 4136/10244 + the 9-site CALL/assign cluster — primsize==0
    →structlookup→nil→fall to normal; route skips the block→same normal.

Install the peellint bare-primsize FINALE (B7 lint-fuse contract):
tools/peellint now rejects any bare primsize() in the ww stage outside
the annotated whitelist.  Evasion-hardened per the B7 lesson — a
character scan (comments + string/char literals stripped first) and a
LEFT+RIGHT word-bounded match of the bare `primsize` TOKEN (not just
`primsize(`), so the aliasprimsize() wrapper is never a hit and every
compiling spelling reds: the call primsize(nm), the paren-wrap
(primsize)(nm), the function-value bind `let p = primsize`, and any
line-split.  ww-only (the C stage dealiases via type_chase_named, no
primsize symbol).  Two independent exemption windows (peel-ok vs
primsize-ok) so neither rule blinds the other.  Runs as a make-test dep.

Whitelist the 6 designed exemptions with primsize-ok WHY-annotations:
  machinery — aliasprimsize body (SSoT chase) | typenodeprimresolved +
    exprprimresolved (#11/#33 prim-resolver chasers) | cgcast leaf-loop +
    cgenexpr #11 deref-store (own ps==0 fallback; route would regress
    #11) | the primsize oracle/definition itself (nothing below to chase).
  structural — elemsizeof x2 + paramfieldsize (chase lives in the -c
    twin elemsizeofc; threading c is the dormant #110).

Empty-flip-set proof: zero C bytes; cstage binaries bit-identical;
bootstrap byte-id 990-997 + 950 all green (w6c == w6c_ww on the full
selfhost, self-rebuild identical); combined.ww (w6c + wwdump) regen
idempotent; sizelint 0; peellint 0 (raw-peel AND bare-primsize over the
whole tree = the close-by-construction proof, zero unwhitelisted
survivors).  Tests: 944_peellint_gate +14 rows (bare / space-before-paren
/ name-at-EOL split / string-blind opener / paren-wrap / fn-value-bind
RED; aliasprimsize wrapper + primsize-ok annotated GREEN; corrupt
annotation RED; independent peel/primsize windows; C-file out-of-scope).

Closes the #101 primsize-alias family by construction.  #109.
2026-06-06 13:06:37 +09:00
4459a49d3a wcc/cgen: #87 plain tagged-union module-global DATA + match SB-resolution (both-stage)
A PLAIN (non-alias) module-level tagged-union global SEGV'd on BOTH
stages: no static DATA was emitted (let_emit_size/letemitsize returned 0
for TY_TAGGED) so the global was never registered, and the match
scrutinee resolved it as a frame-local at offset 0 — reading saved BP as
the tag. Two sub-sites, one route (neither half ships alone — DATA
without SB-resolution still SEGVs; SB-resolution without DATA reads
nothing):

(a) DATA-emitter — a non-nullable TY_TAGGED arm emits the box that
    byte-MIRRORS a runtime LOCAL of the same type: tag word at +0 (the
    const-selected variant index via cg_tag_for_variant / taggedvariant-
    index), payload at +8, zero-padded to the union box size. int and
    str/slice literal variants are wired (str carries a DATAR ptr patch
    at +8); any other variant payload loud-stops (rule 7). emit_tagged_
    data + emittaggeddata are the per-stage twins; let_pre_intern/
    letpreintern gain the matching str-variant intern. Nullable stays 0
    so the (*T|void) one-word fold keeps the 8B scalar arm.

(b) match-scrutinee global resolution — the PLAIN-tagged twin of #78:
    a global tagged ident scrutinee LEAQs name(SB) and copies the box
    into an @match_spill slot the dispatch indexes off BP.

DATA target (mirror of the local box, verified byte-for-byte): for
(i32|str)=42 the 32B box is tag0 | 42@8 | zero-pad; for ="x" it is
tag1 | ptr0@8(DATAR _S_n) | len@16 | cap@24. cs and ww emit byte-
identical asm.

Pins (rob §3, dual-stage 910 cstage + 997 wwstage, attest_pass.ww): the
tagged global-vs-local byte-identity pin (match over the GLOBAL gives the
same arm/value as over a LOCAL — was SEGV both stages) and the str-
variant tag-1 pin, plus the #86 tuple global-vs-local lock-pin guarding
the already-correct emitter path.

929 fail_global_src graduates: a >48B tagged GLOBAL by-value arg now
resolves through the cgplaceaddr MEMORY-class arm (LEAQ g(SB) + blit)
instead of the #38b loud-stop, and runs correctly (uninit zero box ->
first variant); the row becomes a positive run pin. The struct-variant
>48B init still loud-stops via the data emitter.

The first-class-VALUE copy of a tagged ident (`let q = g`) stays a
pre-existing silent #49/#46 sibling (local and global identically),
filed separately — out of this fold's two sub-sites.
2026-06-06 12:05:56 +09:00
3546673756 wcc_ww/cgen: #63 alias-named struct-lit fill via structlookupchain (let-init + sret-return)
cglet's N_STRUCTLIT init arm resolved the struct by a bare
structlookup(c, sname). For an alias-NAMED literal
(`type rep2 = rep; let r = rep2{id=6}`) the type ref carries the
alias name "rep2" but only the base `rep` is registered, so the
lookup returned nil and the field-fill never fired. The nil then
split by slot size into two symptoms of one root:
  - <=8B: the small-let scalar default zeroed the slot and DROPPED
    the literal (SILENT wrong — the field read 0), and
  - >8B: no fill arm matched, falling to the cglet "unhandled rhs
    shape" LOUD (task #7/rule-7).

Route the arm through structlookupchain (the #92/W2 SSoT already
adopted at cgenstmt:1974/:2687), which chases the alias chain to the
base struct. trefn (rhs.lhs) is already the N_IDENT/N_TNAME type ref
structlookupchain accepts, so the bare sname extraction is dropped.
cstage operates on the resolved Type* via type_chase_named and was
always correct: ww-only align-UP, cs UNTOUCHED.

ROUTED (the two reachable silent sites, one class):
  :2421  local N_STRUCTLIT let-init — the #63 repro hits it for
         both the <=8B silent-zero and the >24B loud symptoms.
  :1250  >24B sret RETURN twin (reviewer-63). sretretsize chases
         the alias for the size GATE so this sret arm fires, but
         the fill used the same bare structlookup(sname) — for an
         alias-named >24B literal it returned nil and the fill was
         SKIPPED, so the callee returned an uninitialised sret
         buffer (SILENT wrong, runtime-0; cs correct). Same root,
         same symptom, sibling site → folded by construction.
DECLINED (traced, not blind-routed; rule-11 + the #101 precedent):
  :2625  N_IDENT struct-copy — also bare-structlookup but the copy
         falls through to a generic path byte-identical with cstage;
         both stages run correct. The post-copy field-READ diverges
         (cs direct-offset vs ww LEAQ-indirect) = the #81/#65 alias
         field-read class, out of #63 scope.
  :2511  N_CALL struct-recv — blocked UPSTREAM by the aggregate-
         return shape (#272/#277); ww louds at the sender.
  :1363  <=24B register RETURN — alias case louds via the same
         scalar-default catch (#277), not silently wrong.
The 2 already-chasing sites (1974/2687) untouched.

CONVERGENCE: m3_letinit_typed + m3_letinit_untyped (ww silent-zero ->
6/6 byte-id) + m6_letlit_alias (ww loud -> 7 byte-id) + sret_return_-
alias32 (ww silent-0 -> 10 byte-id), plus a non-alias control
(no-regress). Bootstrap byte-id NEUTRAL (selfhost has no
alias-struct-litinit/return; all 4 selfhost tools cs==ww confirmed).

Test: 944_alias_structlit_init_run (5 rows x cs-run + ww-run +
cs==ww byte-id = 15 checks), Makefile-wired.
2026-06-06 10:48:05 +09:00
45f5415209 wcc_ww/cgen: #101 narrow-alias fill-stride via aliasprimsize SSoT
A struct-literal array fill sized a narrow-alias element off a bare
primsize(name): `type my32 = u32` gave primsize("my32")=0, so the
element width defaulted to 8 and a [3]my32 strode MOVQ -24/-16/-8 —
field n collided with arr[2] (kw1_101 run exit 1). cstage chases
my32->u32->4 (MOVL stride-4) at the twin sites and is runtime-correct;
this is a ww-only align-up, cs untouched.

Fix: a new aliasprimsize(c, nm) SSoT helper — primsize(nm), else an
aliaslookup-chase N_TNAME loop then primsize — and route the SIZE-use
primsize() family through it. The 7 c-bearing bare-no-chase size-use
sites are routed: cgen:993 (letemitsize), cgenstmt:1896 (cgarrlitfillbp),
cgenutil:1579 (elemsizeofc fallback)/1657+1665 (nodeprimwidth)/4791
(cgstructlitfill = the kw1_101 site), cgenexpr:6902 (cgcall vararg esz).
This is the rule-13 close-by-construction shape (one accessor for
"resolved primitive size"), not a per-site patch.

kw1_101 is the SOLE asm mover (byte-id NO->YES, run 1->0, MOVL
stride-4); every other routed site is latent/byte-neutral. Bootstrap:
all 5 combined units stay w6c==w6c_ww byte-identical. sizelint 0,
peellint 0, test-unit 296/296.

Scope fence (rob route-7-decline-6 ruling): three DESIGNED-exemption
sites carry inline primsize-ok annotations — elemsizeof :1475/:1499 and
paramfieldsize :3541 are structural (no-`c`, non-chasing) BY DESIGN;
their alias-chasing twin elemsizeofc is the routed :1579 leg. These are
the #109 peellint-whitelist seeds. Three further declines are already
correct chasing paths, not bare-no-chase bug shapes (typenodeprimresolved
:2026 / exprprimresolved :2063 are the chase machinery itself; cgassign
:7631 already chases via typenodeprimresolved, #11). The ~17 GUARD sites
(is-primitive dispatch) + the peellint finale are the committed #109
follow-on. Threading `c` into the structural sizers is dormant #110.

#101
2026-06-06 10:04:37 +09:00
c9cfa52624 wcc/check: #103/#108 inferred untyped-int defaults to int (8B), both stages
cstage type_default(TY_UNTYPED_INT) returned ty_i32 (4B): an unannotated
`let x = <v>` / `let a = [<v>,..]` silently TRUNCATED any value > 2^31
(5000000000 -> 705032704) and strode inferred arrays at 4. wwstage kept
the element raw untyped_int (size 0), which sized INCONSISTENTLY across
cgen — the array STORE strode the 8 sentinel but letslotsize under-
allocated the frame (SEGV) and cgindex strode the READ at 1. The two
stages were each wrong differently; #263 polarity: cstage was the
truncating side. int = machine word = 8B (Go-style, MEMORY
project_int_machine_word_derived_limits); Hare lowers a flexible iconst
to `int`, never a fixed i32 (ref/harec/src/types.c:835).

Fix, one root, both stages (FUSE — the cs default + the ww concrete
element must land together, else the inferred array is transient cs!=ww):
- cmd/wcc/type.c type_default(TY_UNTYPED_INT) ty_i32 -> ty_int. The
  root; stops scalar AND array truncation at source.
- cmd/wcc/check.c N_ARRLIT empty-elt fallback ty_i32 -> ty_int. Symmetric
  pair; count-0 array emits no stores, so byte-id-neutral.
- selfhost/cmd/wcc/check.ww exprtype N_ARRLIT: default the inferred
  element's untyped flavor to concrete (untyped_int->int, _float->f64,
  _str->str, _rune->rune, _bool->bool, mirror cstage type_default),
  empty-elt "i32"->"int", and stamp the synthesized N_TARRAY's .type_ so
  slotsize / elemsizeofc / letslotsize read its real [N]int size via the
  type table (rule-13) — no letslotsize special-case (SSoT).
combined.ww regen (check.ww embed): w6c + wwdump.

ken v2 corpus re-census (160 files): EXACTLY 5 rows move, ALL CONVERGE
(byte-id YES + run exit 0, none both-wrong, zero regression):
  m2_while   #108 scalar via alias-bool loop
  m8_range1  #104 for-range elem over alias [4]int
  m8_range2  #104 over 2-level alias
  m8_slice1  #103 inferred array + alias-slice init
  m8_slice2  #103 + 2-level-alias slice + re-slice
Bootstrap byte-id neutral (5 combined units w6c==w6c_ww; 0 bare inferred
arrays in selfhost). Annotated controls untouched ([4]i32 stride-4,
[4]int stride-8, byte-id). Pinned in test/wcc/813_arrlit_infer_elem_run
(the 2 direct repros incl the >2^31 truncation teeth + all 5 movers +
controls; test-unit 296).

Closes #103 (inferred-array SEGV + truncation), #108 (cstage scalar
untyped-int truncation), #104 (for-range elem alias i32-stamp), and the
m8_slice []int-init acceptance divergence.
2026-06-06 09:23:24 +09:00
fc50a27f3e cgen: #95 c3 reviewer-fold — is/as gate exact-only, no widening leak
c1/c2 widened flatvariantidxt (selfhost) with the chain + structural
tag-synthesis arms and a >=2 ambiguity os.exit, scoped to the cgen
WIDEN consumer. But flatvariantidxt is a choke-point: the wwstage is/as
ACCEPTANCE gate (check.ww:4677, the #198 spread fallback) reuses it, so
the widening leaked into checker acceptance — vs base 329481c:
  * `let v:(void|ali)=…; v is base` (ali=base): cstage rejects, wwstage
    ACCEPTED+built — new cs!=ww acceptance divergence (rule-10 break);
  * `(void|tb)`, `v is ta` (unrelated same-layout): same leak via the c2
    structural arm;
  * `(ali|ali2)`, `v is base`: wwstage DIED with the cgen-internal fatal
    "flatvariantidxt: source alias chain reaches >=2 variants" DURING
    CHECK — a cgen diag surfacing in the checker (layering).
cstage is unaffected: its is/as gate (check.c:2036) is independent of
cg_tag_for_variant (cgen-phase only), so the fuse was already broken at
this site — the cgen-helper change moved wwstage's CHECKER but not
cstage's. This contradicts the #95 fold scope ("cgen-tag fold, no
acceptance change except the ambiguity hard-error [at the widen site]").

Fix (rob-ruled): the is/as gate needs only nominal variant membership =
pass 1. Add an explicit `exactonly` mode to flatvariantidxt — the
checker caller passes true (returns after the exact loop: no chain/
structural arms, no os.exit), every cgen caller passes false (full
tag-synthesis, unchanged). Two consumers, two modes — the honest
representation, not a wrapper. cstage's cg_tag_for_variant has no twin
checker caller, so it stays full-only and is UNTOUCHED by c3 (rule-10
satisfied: the param changes no asm — cgen always passes false; the
checker now MATCHES cstage's reject). casevariantin still backs the
#198 spread fallback.

Pins (test/wcc/944_variant_chain_b95_run.c, +4 rows -> 56 checks):
  isas_chain_reject / isas_unrel_reject — BOTH stages reject the leaked
  is/as shapes (shared experr substring "not a variant"); the c1 chain +
  c2 structural arms no longer widen acceptance.
  isas_amb_reject_notcrash — the (ali|ali2)/`is base` shape rejects
  CLEANLY (the cgen fatal text would be absent -> red), NOT a crash.
  twin_prim_alias_amb — rob's obligated mixed prim/alias TWIN:
  (int | ai) ai=int, source aj=int — both share the int bottom under
  all-variants counting, so the cgen WIDEN (full mode) hard-errors
  ("source alias chain reaches >=2 variants"), pinned LOUD both stages.

The deferred question (should is/as EVER accept cgen's richer chain/
structural shapes? = a checker-strictness feature, both stages together)
is filed as task #107, explicitly NOT folded here.

Invariants: c1/c2 cgen behavior unchanged (all cgen callers pass false =
full mode); suite byte-id rows + the dissolution corpus hold. make all
0; sizelint 0; peellint 0 (the mode param adds no peel sites); combined.ww
regen idempotent; test-unit "all 295 tests passed". c3 touches ZERO
cstage bytes — cmd/w6c/cgen.c carries only the c1/c2 additions, and
cmd/wcc/check.c is unchanged from base 329481c.
2026-06-06 08:07:03 +09:00
56aac85f6f cgen: #95 c2 structural variant fallback — both-stage fused
A nominally-unrelated, structurally-equal NAMED source into a NAMED
variant (kb95_unrel: ta/tb same-layout structs, src ta -> (void|tb))
was LIVE both-wrong-identical byte-id silent: both checkers accept,
both cgens tagged 0. After c1's chain arm finds no shared chain
node, match the variant whose CHASED type type_eq's/typeeq's the
source's chased bottom — chased type EQUALITY only, no
type_is_assignable scalar import, no int widening (ken's binding
scalar warning). Same NAMED-source branch, both stages
(cg_tag_for_variant / flatvariantidxt), forced fuse.

Correctness reference, cite 1 — harec tagged_select_subtype P2+P3
(ref/harec/src/types.c:702-739), verbatim:

	if (t->id == subtype->id) {
		return t;
	}
	if (type_is_assignable(ctx, t, subtype)) {
		selected = t;
		++nassign;
	}
	...
	if (nassign == 1) {
		return selected;
	}
	return NULL;

with type_is_assignable's non-tagged path dealiasing both sides and
accepting composites only via interned pointer equality
(types.c:988-1002), verbatim:

	if (type_dealias(ctx, to)->storage != STORAGE_TAGGED) {
		to = type_dealias(ctx, to);
		from = type_dealias(ctx, from);
	}
	...
	if (to == from && to->storage != STORAGE_VOID) {
		return true;
	}

Cite 2 — type_hash interns bare composites STRUCTURALLY (banked as
types.c:72-81; verified in the vendored copy at types.c:444 +
struct/union arm :514-525), verbatim:

	case STORAGE_UNION:
		hash = fnv1a_size(hash, type->struct_union.packed);
		for (const struct struct_field *field = type->struct_union.fields;
				field; field = field->next) {
			if (field->name) {
				hash = fnv1a_s(hash, field->name);
			}
			hash = fnv1a_u32(hash, type_hash(field->type));
			hash = fnv1a_size(hash, field->offset);
		}

— no decl ident in the hash, so harec's two decls dealias to ONE
interned node and `to == from` holds: acceptance is DEFINITIONAL
under interning, not an arm whose text could be misread. Our store
does not intern; chased type equality is the non-interned rendering
of the same rule.

Honest divergence (the >=2-structural-match hard-error STAYS): under
harec's interning two structurally-identical variants are ONE type —
a union cannot contain it twice — so the ambiguity case is
unrepresentable there; our hard-error (twin texts, shared tail
"source structurally matches >=2 variants — ambiguous without
nominal layout (#95)") is the correct nominal-lossy-model rendering,
not a harec deviation.

Pin table: unrel_struct row added (kb95_unrel graduates ok/1-ok/1 ->
0/0, byte-id held) — suite now 48/48. All c1 rows unmoved.

Invariants: 163-row dissolution matrix at tip — same 3 family
graduations as c1, ZERO new movers; five mains cs-vs-ww byte-id OK;
make all 0; sizelint 0; peellint 0 (no new peel sites — the
structural leg reads only chased ends); all 944 suites + 808 green.
w6c_ww/wwdump_ww main.combined.ww regen'd.
2026-06-06 07:43:39 +09:00
34c86bd681 cgen: #95 c1 chain-membership variant arm — both-stage fused
A NAMED struct source that was not pointer-identical to a NAMED
variant fell through every pass of cg_tag_for_variant (cmd/w6c/
cgen.c) / flatvariantidxt (selfhost/cmd/wcc/cgenutil.ww) and the
widen stored tag 0 — both stages, byte-identical, gate-blind: wrong
tag on VALID code at any alias depth, in both chain directions
(.ai/ken-95-oracle.md §2: kb5_v2s1i, kb95_2lvl_i, kb95_deep_src,
kb95_deep_var all both-wrong-identical at base).

New pass 1b, identical both stages (the same route — forced fuse):
after pass-1 exact (unchanged, FIRST — the (str|linerr) protection,
harec's P1 short-circuit), a NAMED source matches the variant whose
NAMED chain shares a pointer-identical node with the source's chain
(an alias IS-A its base through the chain). Two linear NAMED chains
intersect iff they share their chased bottom node (ken §1), so the
walk is implemented as pointer identity of the chased ends through
type_chase_named/tichase — the blessed chase choke-point. NO raw
.under/->under hops were added, so the anticipated `peel-ok: nominal
chain walk (#95)` annotations are unnecessary and the peellint
whitelist is UNCHANGED (continues the B6/B7 fold-peels-into-chase
arc; peellint green).

Variants are counted UNGATED (bare prims are type-table singletons,
so a bare variant node can BE the source's chased bottom): the >=2
guard stays equivalent to harec's nassign>=2 -> NULL
(ref/harec/src/types.c:734-738, tagged_select_subtype P2/P3). >=2
chain hits hard-error with twin texts (prefix convention, shared
tail "source alias chain reaches >=2 variants — ambiguous without
nominal layout (#95)") — drew's ambiguity proviso extended to the
chained set; was a SILENT member-0 tag. Pass-2 bare-source fallback
unchanged. Chased type EQUALITY only — no type_is_assignable scalar
import, no int widening (ken's binding scalar warning).

Pin table (new suite test/wcc/944_variant_chain_b95_run.c, 45
checks, Makefile-wired):
  GRADUATIONS exit 1->0 both stages: chain_1lvl_i (kb5_v2s1i
  HEADLINE, byte-id held), chain_2lvl_i, chain_deep_src,
  chain_deep_var (byte-id held), chain_call_bound81 (kb5_v2s1),
  chain_call2_bound81 (kb4_v2_struct2, #95's original) — the two
  CALL-src rows waive byte-id, pre-existing #81 zero-fill asm noise
  (NO at base too).
  NEW LOUD: chain_amb_loud (kb95_amb) — silent tag 0 -> hard-error
  both stages.
  MUST-NOT-MOVE held: chain_amb_srcA/B (pass-1 precedence),
  nom_str/nom_err (#218 nominal regression pin), exact_ctl
  (kb5_v2sE2), bare_ctl/bare_2lvl/bare_ambig/bare_ambig2 (pass-2
  controls), callret_bound277 (kb5_v2sE #277 cells unchanged,
  dual-cell pin).

Invariants: ken's 163-row dissolution matrix rerun — exactly 3
movers, all family graduations (v2s1i/v2s1/v2_struct2 1->0), zero
non-family movers, detectors unmoved. Five mains cs-vs-ww byte-id
OK (ww/w6c/w6a/w6l/wwdump). make all 0; sizelint 0; peellint 0; all
944 suites + 808 green. w6c_ww/wwdump_ww main.combined.ww regen'd
(cgenutil.ww embeds).
2026-06-06 07:40:32 +09:00
329481c920 wcc_ww/check: W3 #105 nested-arrlit gate chases the alias elem type
checkarrlitfits' nested recursion keyed on the raw elemtn kind; a
named-alias element type ([2]row, row=[2]int) arrives as N_TNAME, so
the inner overlong literal skipped the count+range checks and the
module static-DATA route emitted silently TRUNCATED data (ken's
m7c_global: DATAW 1,2,4,5 — exit-masked once the #60 read fix removed
the segv; cstage loud-rejects every spelling via its typed-literal
assignability net). #105: the W1 fill gate never runs on this route,
severity raised post-#60.

Fix: chase elemtn through resolvealias (transitive) at the recursion
gate — alias spellings of any depth take the same checks as the
direct shape at all four contexts funneling through the choke point
(module let / local let / def / struct-field). A direct N_TARRAY
passes through resolvealias unchanged, so accepted shapes are
byte-identical base→tip (m7c_global_ok + exact-fit alias
field/def/2lvl probed ASM-ID vs a base scratch build). The m7/m7b
local overlong rows stay loud, now via the earlier count-naming
checker diagnostic instead of the cgen #270-1c fatal. The
out-of-range narrow inner element louds "array element out of
range" exactly as the direct spelling already did on wwstage.

808_arrlit_overlong: 37 -> 50 checks (+1 accept control
alias_exact_module = ken's m7c_global_ok with a byte-id cell, +4 loud
flips alias_nested_{module,2lvl,def,field} pinning per-stage texts,
+1 REVIEW AMENDMENT alias_nested_local pinning the m7/m7b text move
— pre-fix ww was loud via the late cgen #270-1c fatal; the row reds
if the diag regresses off the checker count text).
989 ratchet zero flips — no lib module-level literal trips the gate.

Filed sibling, not folded: OUTER alias-of-array overlong
(let g: arr = [5 elems], arr=[4]int) still ww-silent-truncates at the
alias-blind call-site N_TARRAY gates; cs louds with the count text.
2026-06-06 07:05:47 +09:00
4b118fa8f8 cgen: B7 emitter elem chases + tools/peellint gate — #5 alias-arc cs side closed by construction
The last four raw `->under` reads outside the whitelist were the
static-DATA emitters' ELEMENT-type single peels (the outer type already
chased): emit_array_lit_bytes:14356, emit_strarray_data:14574,
emit_slice_data:14788, let_pre_intern:15088 -> type_chase_named.
:15088 is the :14574 row's label-order leg and must flip in the same
commit or _S_ labels intern in emit order, not decl order (the in-tree
comment at the site); the strarr row's byte-id is the coupling proof.

Behavior moves (ken B7 first-position oracle + impl pre-state, all
pre-observed at 05f7af7):
- [N]alias-struct + [N]alias-str globals graduate cs link-ERR
  ("undefined reference") -> 0/0 BYTE-ID (cs emits ww's DATAW).
- zero-consumer latent silence closed: a never-referenced
  2-level-elem-alias global silently lacked DATA (no reference, no
  link error); now emits, pinned by the byte-id cell.
- []alias-str diagnostic routing: the alias escaped the 3-way
  slice-of-{str,slice,tagged} fatal onto the downstream "not a
  foldable constant" text — now the intended 3-way text (== control).
- []alias-tagged DESIGNED NARROWING: the alias dodged the 3-way fatal
  ENTIRELY — cs silently accepted + RAN WRONG for reachable consumer
  shapes (review-verified at base: a len+payload-read probe exits 1;
  the len-only row was luck-correct). Now loud with the 3-way text;
  widen what the gate SEES, never what it ACCEPTS (B6-c2 precedent).
- kb7_slc/slc0 scalar legs byte-NEUTRAL (the synthesized-array
  choke-point already handled them); full kb corpus sweep: movers are
  exactly the two graduation shapes, nothing else.

tools/peellint (sizelint clone, dep of test/test-unit): character-scan
strips comments and string/char literals, then matches the under-token
accessor-spelling-wide — `->under`/`.under` in C (deref-dot is the
same peel), `.under` in ww, optional whitespace after the operator,
and the line-split continuation (operator at EOL, `under` next line).
Scope cmd/wcc + cmd/w6c + selfhost/cmd/wcc + lib/ww (lib/ww/typ.ww
ruled IN — it is type.c's ww mirror, the accessor layer itself);
`peel-ok`/`peellint-ok` annotations exempt a 10-line window. Green at
this tip = zero unwhitelisted raw peels survive; the gate lands in the
commit that deletes the last raw read (the-funnel-completing-commit-
carries-the-gate; sizelint precedent). Whitelist, 27 entries:
  cmd/wcc/type.c    :78 :141 construction, :162 chase body,
                    :180 :193 :214 recursive chase
  cmd/wcc/check.c   :102 :2572 resolve-state probes, :2586 construction
  cmd/w6c/cgen.c    :731 probe-cleared scan peel (B5-c1),
                    :813/:814 :834/:835 peel-ok #218 variant-match
  lib/ww/typ.ww     :316 construction, :374 :385 :410 :437 :447 :463
                    :475 :488 :514 recursive chase
  selfhost/cmd/wcc/cgenutil.ww :1302 chase body (tichase),
                    :2759 probe-cleared peel
  selfhost/cmd/wcc/check.ww    :1815 construction (peellint-ok)

Negative validation wired into 944_peellint_gate (B4 precedent):
re-introduced raw peel (C and ww spellings) REDS the lint; corrupted
annotation (peel-okk-…, token-bounded matcher) REDS the lint; the
check.ww:3683 "io.underread" prose, a code read of a longer field, and
comment-quoted tokens are pinned green regression rows; real tree must
lint clean. 944_alias_emit_b7_run pins all four emit paths
table-driven (14 rows / 36 checks) incl. ken's ww observation cells
(ww checker rejects slice-literal globals, "let: not assignable" —
unmoved; plain []str louds at ww's own emitslicedata 3-way, pinned by
the shared needle).

REVIEW AMENDMENT (reviewer-B7, fix-what-you-find): the frozen tip's
regex matcher passed five compiling evasion spellings green — `t ->
under` spacing, `t->`/EOL + `under` next-line (both stages; ww parses
`t.`/EOL too), C deref-dot `(*t).under`, ww `t. under`, and a string
literal containing a block-comment opener that blinded the regex
comment-strip for the rest of the file. The matcher is now a
character scan (comments + string/char literals stripped before
matching) with the widened token rule above; all six spellings are
pinned RED rows in 944_peellint_gate (checks 10 -> 16). The 10-line
annotation window stays as designed (a peel within an annotation's
window is exempt by construction — the window IS the exemption
mechanism). Lint + test bytes only; zero compiler-source bytes moved
in review.

What this does NOT close, said out loud (f2-ruling): a consumer that
never spells `under` at all — a switch on t->kind that simply never
peels — has no token for the lint to see. The accessor+lint closes the
WRONG-PEEL class (single-peel where chase was needed) by construction;
the NO-PEEL class is closed only at sites where classification routes
through the internalized chasing helpers, and contained elsewhere by
the acceptance-commit-carries-tripwires doctrine, which stays standing
for every future acceptance widening. The gate does not make alias
bugs impossible; it makes the four-times-burned shape unwritable.

Rule-11 note: forced fuse — the four conversions ARE the last raw-read
deletions; peellint cannot be green one commit earlier (consumer-graph
-forces-the-fuse precedent, #61).

Invariants: cs asm byte-NEUTRAL on the whole bootstrap corpus (five
mains + smoke, base-input pre==post); five mains cs==ww byte-id at
tip; _ww binary quartet bit-identical to the W2 baseline (ww changes
are comment-only annotation bytes — codegen-inert, proven by the md5
hold); w6c_ww+wwdump main.combined.ww regen'd via make, idempotent;
989 lib ratchet zero flips (31 byte-id / 9 pinned-divergent / 3
pinned-wwreject across 43 units); sizelint 0; peellint 0;
make test-unit "all 294 tests passed" (292 + the two new suites).
2026-06-06 06:05:46 +09:00
05f7af76d1 wcc_ww/cgen: W2 #102 indexed-elem field store + &-field gates chase the alias chain
The two ww-side gates from ken's B6-c3 STOP re-attribution
(.ai/ken-b6-oracle.md addendum): WWSTAGE was the alias-blind side at
exactly two cgenexpr.ww sites, both keyed on a bare name-keyed
structlookup that only holds struct-decl names, so an alias-NAMED
spelling missed and fell to the generic-but-runtime-correct route —
byte-divergent from the canonical dedicated shape cs pins post-B6-c3:

  (1) `arr[i].f = v` indexed-elem struct-field STORE arm (the write
      twin of the task-#8 READ arm, which already chases via tichase);
  (2) `&p.f` ptr-field fallback in the TK_AMP N_DOT single-dot leg.

REVIEW AMENDMENT (reviewer-W2, rob's close-by-construction
obligation): the proof-sweep of the two arms found ONE same-class
survivor — (3) the value-struct `&x.f` leg, one leg below (2) in the
same single-DOT arm, same alias-blind bare structlookup. Probe
rW2_ampv reproduced the identical bit-proven mechanism (ww_alias !=
ww_plain; cs_alias == ww_plain) at both base and the frozen tip; the
other in-arm routes are clean (letvar* global helpers chase, str/len
pseudo-field alias legs are filed-loud #96, chained depth>=2 resolves
via tinfo). Same chase applied; arm survivor count is now zero by
the same grep+probe construction.

All three now resolve through structlookupchain (#22) — the name
layer's own alias-chain accessor (the cstage transitive-peel mirror),
already consumed by the sibling `p.f = v` assign arm and cgdot's
pointer-to-struct read. Plain rows short-circuit at its structlookup
head, so non-alias emission is byte-identical by construction; alias
rows land on the same structinfo as their plain twins, so the
dedicated emission converges bit-exactly. esz stays sound:
elemsizeofc reads the chased stamped tinfo (#8 leg).

Convergence proven with ken's pre-staged W2 instrument: ww_alias ==
ww_plain bit-IDENTICAL for kb6_idxf and kb6_ampf (cmp exit 0), which
also lands cs==ww byte-id on both rows; plain controls byte-id held
and their ww asm unchanged base->tip. Full ken-corpus matrix vs the
B6-tip baseline: movers are exactly {kb6_idxf, kb6_ampf} NO->YES;
detectors unmoved (kw1_101 cs0/ww1, fill2 both-loud, tuparg_c
cs0/ww1, fsarg2/fsarg0/try pinned texts verbatim, B5/B6 graduations
all byte-id). Zero behavioral change on any accepted program, zero
acceptance change in either direction.

ww-only: zero C-source bytes; cs binaries (w6c/ww/w6a/w6l) md5
bit-identical base->tip; w6c_ww + wwdump main.combined.ww regen'd via
make, idempotent. 989 lib ratchet: zero flips (31 byte-identical /
9 pinned-divergent #59 / 3 pinned-wwreject #59 across 43 units).

944_alias_cgen_b6_run: idxf_2lvl + ampf_2lvl graduate K_RUN_NOID ->
K_RUN (the two restored byte-id cells) — B6-c3's mutation teeth
restored (reviewer-B6 N-a closes); + amendment rows ampv_2lvl /
ampv_plain_ctl pin the third gate (checks 61 -> 69). All 9 944-family
suites green; sizelint 0; make test-unit "all 292 tests passed".
2026-06-06 05:11:29 +09:00
0077b115fb cgen: B6-c5 reads/len/globals family single peels fold into type_chase_named — 8 lines, B6 territory closed
The exact B6-c5 set (rob b6 spec §2, numbering at 4cac1cb): :3820
(fn-symbol load u), :7201 (#235 len() tuple-elem bu), :7807
(type_default let-init region u), :7900 (append su, str→[]u8 region),
:9944 (`is` source u — the #37 memread predicate feed; the >32B cap
test now keys the CHASED size, mirroring ww's taggedmemread, per the
EYES condition — kb6_memread 40B-union byte-NEUTRALITY is the
regression net, held), :10224 (module-qualified value ref tu),
:10750/:10873 (tagged-union field loads, direct + through-ptr
tag_fu/ptag_fu). Raw `->under` in cgen.c 17→9 — the remaining 9 are
EXACTLY the designed whitelist survivors (:731 nullable_ptr_tag +
:813/:814 + :834/:835 cg_variant_match peel-ok-#218, #95's fold) and
B7's 4 emitter/intern lines (:14356 :14574 :14788 :15088). B6's 49
granted lines are fully retired; grep-verified.

FLIP: kb6_tfread (tagged-union field load direct + through-ptr over
alias struct, was both-correct divergent NO(4)) → 0/0 BYTE-ID; cs
converged onto ww's UNCHANGED asm (#263 polarity, cmp-proven) and the
alias shape equals the plain shape (kb6_tfread_p, same hash — ken's
c5 pre-test). kb6_len / kb6_gref / kb6_memread latent byte-NEUTRAL
as predicted.

TRAIN INVARIANT (held c1..tip): cs-only; w6c_ww/ww_ww bit-identical
to the 4cac1cb baselines (b6bddc8eb5c3ed8e805e50371d4b7017 /
4e9ca8741f19e1f68219ff799a5e5a14) at all five boundaries. cs movers
c4→c5 bounded to exactly {kb6_tfread}; rest of corpus + five mains
byte-NEUTRAL; kw1_101/fill2/tuparg_c/xampdef/amplen1 detectors
unmoved across the whole train; 989 ratchet zero flips.

944_alias_cgen_b6_run final table: 18→22 rows, 49→61 checks
(tfread_2lvl flip + len_2lvl/gref_2lvl controls + memread_40b cap-
watch pin). All 944-family suites green; sizelint 0.
2026-06-06 04:28:23 +09:00
c136f3c0f7 cgen: B6-c4 cast/is/try family single peels fold into type_chase_named — 10 lines
The exact B6-c4 set (rob b6 spec §2, numbering at 4cac1cb): :9422
(N_MATCH Family-C identity-cast peel su), :9598 (match-bind base bu),
:9674 (tryprop non-call source u), :9718 (cg_ret_type peel r at the
cgexpr try region — same rt-route kinship as B5-c3's cgreturn chase:
the nullable/tagged propagate keys the chased enclosing return type
exactly as the return arms do), :9819 (tryunw twin u — LOUD-PRESERVING
ONLY, the #38b sret bound is alias-INDEPENDENT), :9986/:9991
(N_TYPEASSERT u + enum vu), :10103/:10105 (str→[]u8 cap-synth tu/fu),
:10131 (narrowing-cast tu). Raw `->under` in cgen.c 27→17 — remaining
= whitelist 5 (:731 :813/:814 :834/:835) + B7 emitters 4 + the c5
set 8, grep-verified exact.

FLIP: kb6_strcast (str→[]u8 over 2-level alias, was both-correct
divergent NO(2)) → 0/0 BYTE-ID; cs converged onto ww's UNCHANGED asm
(#263 polarity, cmp-proven) and the alias shape now equals the plain
shape (kb6_strcast_p, same hash — ken's c4 pre-test confirmed ww's
dedicated gate DOES fire on alias here, unlike the c3 sites).
kb6_try loud held both stages with the pinned #38b text (try_loud_38b
row); idcast/is/succ byte-id held; zero other movers.

TRAIN INVARIANT: cs-only; w6c_ww/ww_ww bit-identical to the 4cac1cb
baselines (b6bddc8e…/4e9ca874…). cs movers c3→c4 bounded to exactly
{kb6_strcast}; rest of corpus + five mains byte-NEUTRAL; detectors
unmoved; 989 ratchet zero flips.

944_alias_cgen_b6_run grows 14→18 rows, 38→49 checks: strcast_2lvl
flip + idcast_2lvl/is_2lvl controls + try_loud_38b both-loud pin.
All 944-family suites green; sizelint 0.
2026-06-06 04:28:23 +09:00
7a85210b13 cgen: B6-c3 addr-of/field-walk/index spine single peels fold into type_chase_named — 14 lines
The exact B6-c3 set (rob b6 spec §2, numbering at 4cac1cb): :3949
(&ident classify ou), :4006 (&mod.G leaf lu), :4124/:4128 (&p.f
ptr-field fallback bu/inner), :5029/:5035/:5052 (indexed-elem
struct-field STORE elemu/inner/bu), :5937 (assign-region esub peel),
:10953/:10956 (chained N_DOT ptr lu/inner), :11046/:11052/:11067
(indexed-elem struct-field READ twin), :11285 (index-region esub peel
— gates key the chased esubu exactly as the ident arm, per the EYES
condition). Raw `->under` in cgen.c 41→27.

RE-ATTRIBUTION (ken c3-STOP addendum, adjudicated on his independent
scratch build): kb6_idxf and kb6_ampf did NOT land on the oracle's
predicted post-states — and the train direction is CORRECT anyway.
Bit-proven mechanism: post-chase cs_alias asm == ww_PLAIN (the
byte-id-gate-proven canonical dedicated shape) for BOTH rows; it is
WWSTAGE that is alias-blind at these two sites (ww_alias != ww_plain
— its indexed-elem struct-field store/read gates and the &p.f
ptr-field fallback don't fire on alias bases and fall to generic-but-
runtime-correct routes). Converging cs onto ww_alias would re-blind
cs — canonical is the convergence target. ampf's pre-c3 byte-id was
both-stages-on-the-generic-route identity (gate-blind, #263-class),
NOT M5 latency — oracle self-correction banked. Both rows pinned
K_RUN_NOID (cs-0 + ww-0, byte-id waived) + plain-control rows pin the
convergence target; they graduate to full byte-id when the filed
ww-side W2 fold (task #102) lands (ww gate chase; kw1/#100/W1 precedent; zero
metric-1 content). chdot/esub byte-NEUTRAL bound held.

TRAIN INVARIANT: cs-only; w6c_ww/ww_ww bit-identical to the 4cac1cb
baselines (b6bddc8e…/4e9ca874…). cs movers c2→c3 bounded to exactly
{kb6_idxf, kb6_ampf} — the named c3 family; both run-cells ok/0 held;
rest of corpus + five mains byte-NEUTRAL; detectors unmoved; 989
ratchet zero flips.

944_alias_cgen_b6_run grows 8→14 rows, 22→38 checks: idxf_2lvl/
ampf_2lvl NOID pins + idxf_plain_ctl/ampf_plain_ctl canonical-shape
pins + chdot_2lvl/esub_2lvl controls. All 944-family suites green;
sizelint 0.
2026-06-06 04:28:23 +09:00
9659a0dfbe cgen: B6-c2 call-arg family single peels fold into type_chase_named — 8 lines, loud-preserving
The exact B6-c2 set (rob b6 spec §2): :8402 (callee fn-type resolve cu),
:8435 (variadic slice param vsu), :8561/:8563 + :8577/:8579 (tagged
widen-detect pu/au pairs, arg-class + #38b MEMORY-class) + the two
LOUD-PRESERVING chases :8829 (float-struct rule-7 fatal st) and :8934
(#32 tuple-arg rule-7 fatal targ). Raw `->under` in cgen.c 49→41.

LOUD-PRESERVING discipline: the :8829/:8934 chases widen what the gate
SEES, never what it ACCEPTS. DESIGNED ACCEPTANCE NARROWING (ken b6
oracle c2): kb6_fsarg2 — a 1-level-alias float-struct from a non-ident
source previously DODGED the #271/#165 fatal via the single peel; cs
accepted and GP-passed it runtime-correct by self-consistent luck
(caller+callee agreed on the wrong transport, no SSE eightbyte). Post-
c2 cs louds with the pinned #271/#165 text. ww's cell was already loud
at its own alias-return bound (#272/#276/#277 class) — fsarg2_bound
pins BOTH texts per-stage (experr_ww). fsarg0 plain control stays loud
both stages. :8934 is WATCH-ONLY (alias tuple-args are checker-blocked
upstream, #86/#99): kb5_tuparg_c two-key cells verified unmoved
(cs ok/0 + ww ok/1).

TRAIN INVARIANT: cs-only; w6c_ww/ww_ww bit-identical to the 4cac1cb
baselines (b6bddc8e…/4e9ca874…). cs movers bounded to exactly
kb6_fsarg2 (run-cell ok/0→ERR, no asm emitted — zero run-row movers);
rest of the corpus + five mains byte-NEUTRAL; kw1_101/fill2/tuparg_c/
xampdef/amplen1 detectors unmoved; 989 ratchet zero flips. kb6_sarg /
kb6_strarg / kb6_fsarg (ident twin) latent byte-NEUTRAL per ken's
structural bound.

944_alias_cgen_b6_run grows 3→8 rows, 9→22 checks: fsarg0_loud_ctl +
fsarg2_bound (per-stage experr pins; row struct gains experr_ww for
two-site loud pairs) + fsarg_ident_ctl/sarg_2lvl/strarg_2lvl controls.
All 944-family suites green; sizelint 0.
2026-06-06 03:51:34 +09:00
1f14becdf3 cgen: B6-c1 assign/reassign family single peels fold into type_chase_named — 9 lines, cs-only
The exact B6-c1 set (rob b6 spec §2): cgexpr :6359 (tagged-local plain
reassign lu), :6403/:6405 (deref-target assign pu/vt), :6460/:6462
(deref compound-assign pu/vt), :6518 (str/slice/struct reassign lu) +
cgstmt :11696 (nomem null-propagate r), :12047 (assign base peel bu),
:13625 (destructure-reassign rhs ru — chased; the #64 citation above it
stays, the deferral is about the tuple-literal rhs ROUTE, not this
peel). Raw `->under` in cgen.c 58→49.

TRAIN INVARIANT: cs-only — zero selfhost/ or lib/ bytes move; w6c_ww/
ww_ww bit-identical to ken's 4cac1cb baselines (md5
b6bddc8eb5c3ed8e805e50371d4b7017 / 4e9ca8741f19e1f68219ff799a5e5a14).
cs movers bounded to exactly: kb6_streassign, kb5_wstore_a (the
named c1 family); the rest of the kb4/kb5/kb6/kna corpus + five
selfhost mains byte-NEUTRAL both stages; kw1_101 / fill2 / tuparg_c /
xampdef / amplen1 detectors unmoved. 989 lib ratchet: zero flips.

LIVE graduation: kb6_streassign (the :6518 lu single-peel missed
TY_STR at 2 alias levels, fell to the scalar default — `b = a` copied
the ptr WORD0 only, len/cap stale, cs silent exit 1; ww was the
runtime-correct full 3-word reference) → 0/0 byte-id. Designed
graduation: kb5_wstore_a (ken C1-CORR-2 seed — the ident-lhs N_ASSIGN
tagged gate :6359 is cgexpr INLINE, never reached B5's :2456 funnel)
→ wstore_a_2lvl re-pinned K_RUN_NOID→K_RUN in the b5 suite (84→85
checks). kb6_sreassign / kb6_dassign latent controls byte-NEUTRAL as
ken's structural bound predicts.

New 944_alias_cgen_b6_run row table (3 rows, 9 checks): streassign_2lvl
+ sreassign_2lvl/dassign_2lvl controls; Makefile wires
test_alias_cgen_b6_run into the unit list. All 944-family suites green;
sizelint 0.
2026-06-06 03:48:54 +09:00
4cac1cbb89 wcc_ww/cgen: W1 #100 struct-lit fill gate keys off the chased stamped tinfo
The ww twin of B5-c1's :3195 cg_structlit_fill chase — closes the
R4-blessed one-train acceptance divergence (cs-loud / ww-SILENT-WRONG
on alias-element array fills, ken FLAG-3 / kb5_fill2). The #249 fill
arm's loud-gate element classification was alias-blind on two of four
keys: a raw N_TSLICE kind test and a structlookup leaf-name probe —
`type el = el0;` matched neither, bypassed the gate, and fell to the
scalar tail (one word per element, silent exit 1, gate-blind #263
class). Now: issliceel via isslicetype (stamped tinfo, the str/tagged
keys' existing route) and isstructel via tichase + TY_STRUCT kind —
the tnodeisagg discipline the neighboring #49 arm already uses, twin
of cstage type_chase_named at cgen.c:3179. NO new helper.

Loudness-alignment only, zero acceptance movement elsewhere: probe
matrix holds direct str/slice/struct/tagged elem LOUD (twin texts),
int/alias-int/f64 elem fills build + run 0/0 byte-id, 1- and 2-level
alias struct/slice/tagged elems now LOUD both stages.

ww-only: cs side untouched (zero C bytes; w6c up-to-date across the
edit). B5 detector rows hold post-B5 expected state (targ/tmem/wpush/
null/succ/f32p/def93 all 0/0 byte-id). 989 ratchet ZERO flips
(31 ID / 9 pinned-divergent / 3 pinned-wwreject). combined.ww regen'd
for both embedded targets (w6c_ww, wwdump).

944_alias_cgen_b5_run: fill2 graduates the dual-cell K_CSERR_WWRUN
pin to the K_BUILDERR LOUD-HOLD pair (fill0-class, shared experr
tail); fill0 control holds. Check count holds at 84/84 — the kind
flip is cell-count-neutral (2 driver cells either side, no byte-id
cell). All 944-family suites green; sizelint 0; test-unit all 291
passed. Closes #100; the R4 divergence window closes with it.
2026-06-06 03:09:54 +09:00
837fdb4753 cgen: B5-c4 #93 deref-index pointee chase — the one spurious MOVQ, #263-polarity
Trace at the c3 tip: cs-vs-ww diff on l2_local/kb5_def93 = exactly ONE
line, a spurious `MOVQ (AX), AX`. The deciding site is the cgexpr
N_UN(STAR) pointee classify (`ru`): the single peel left a 2-LEVEL
alias pointee TY_NAMED, the ARRAY skip (#61-C — an array value IS its
address, #270-1a) missed, and the scalar load pulled a[0]'s VALUE as
the index base — wild pointer, SIGSEGV 139 on cs. KEN #263-POLARITY:
cs is the WRONG side; ww chases and is the runtime-correct reference —
cs converges on WW's asm. Single-site grant: the one `ru` computation
(shared by the FN/ARRAY/TAGGED skip predicates) → type_chase_named.
Raw `->under` in cgen.c 59→58. #93 CLOSES.

TRAIN INVARIANT holds at the tip: cs-only; _ww binaries bit-identical
to the bcd948d baseline md5s across all four commits. cs movers vs the
c3 tip bounded to EXACTLY the deref-index shapes: l2_local, kb4_x93,
kb5_def93. Zero ww movers. Detector pinned: kb4_xampdef STAYS 139/139
(#94, out-of-train — `&D[i]` indexed def base, a different site).

Graduations (cs SEGV-139 / ww 0, BYTE-DIVERGE → 0/0 BYTE-ID):
g93_l2_local (the banked rob spelling), g93_def (kb5_def93, the
natural `(*p)[2]` def twin). g93_1lvl_ctl (1-level control) held 0/0
byte-id throughout.

944_alias_cgen_b5_run 28→31 rows (84 checks); 944 family green;
sizelint 0.
2026-06-06 02:26:29 +09:00
140755d221 cgen: B5-c3 #89 cgreturn return-position widen — rt/vu chase, trace-first
Trace at the c2 tip (rebuilt binaries, #80-c4 form): ret_widen's mk()
emitted `MOVQ -16(BP),AX / MOVQ AX,DX / MOVQ $0,CX` — the scalar
shuffle arm, payload word 1 ZEROED (s.b/e.aux dropped, cs silent
exit 1). Deciding predicate: the vu single peel left an alias struct
source TY_NAMED → isstruct false → scalar arm. The rt single peel was
the succ half: a 2-LEVEL alias return type stayed NAMED → the whole
tagged-return block was skipped → no tag synthesis at any return
(kb5_succ's three paired return-position insertions). Fix = the three
granted peels (bare-return rt, value-return rt, vu) →
type_chase_named; the route predicates (istagged/isstruct/istuple/
passthrough) key on the chased vu. Raw `->under` in cgen.c 62→59.
#89 CLOSES.

TRAIN INVARIANT holds: cs-only; _ww binaries bit-identical to the
bcd948d baseline md5s. cs movers vs the c2 tip bounded to EXACTLY the
return-route family: ret_widen, kb4_xret, kna_ret_errunion, kb5_succ.
Zero ww movers; detectors pinned (kb4_xampdef stays 139/139 #94;
def93/x93/l2_local stay cs-SEGV — c4's targets; fill/tuparg/v2
families unchanged).

Graduations (cs1/ww0 BYTE-DIVERGE → 0/0 BYTE-ID): g89_ret_widen (the
banked spelling), g89_ret_errunion (the live e.aux truncation seed),
and succ_2lvl flips K_RUN_NOID → K_RUN exactly as C1-CORR-1 predicted
(pins-follow-the-layer). g89_ret_named_ctl (bare NAMED control) held
0/0 byte-id throughout.

944_alias_cgen_b5_run 25→28 rows (75 checks); 944 family green;
sizelint 0.
2026-06-06 02:23:18 +09:00
e36af4fe7e cgen: B5-c2 #73 graduation — the 6 tripwired field gates chase, tripwire deleted
Close-by-construction replacing containment — the designed graduation
path from F1-c1's commit body. The 6 fld_alias_tripwire call sites
(indexed-elem field store/read, heap struct-lit field fill, tuple-elem
read, ptr-chain field read, static struct-lit emit) chase their fu
through type_chase_named; the 6 tripwire calls AND the helper itself
(incl. its :447 peel) are DELETED. Raw `->under` in cgen.c 69→62.
#73 CLOSES.

TRAIN INVARIANT holds: cs-only; w6c_ww/ww_ww bit-identical to the
bcd948d baseline md5s (28ad889042bad8006f1997cbcec94805 /
4e9ca8741f19e1f68219ff799a5e5a14). ZERO new corpus movers vs the c1
tip (kb*/kna corpus + five mains byte-NEUTRAL — the chased gates only
fire on 2+-level alias aggregate fields, none in corpus).

Gate-arm graduations (pre = loud "#73" fatal on cs, ww ok/0; post =
0/0 BYTE-ID): slice/str/tagged arms at the indexed STORE gate
(g73_idxstore/g73_strfield/g73_tagfield), slice arm at the indexed
READ / ptr-chain READ / tuple-elem READ gates (g73_idxread/g73_ptrread/
g73_tupread), nested-struct arm at the static emit gate
(g73_static_struct). The existing 944_alias_accept_run tripwire bound
row graduates K_BUILDERR_CS → K_RUN.

Two arms cannot pin the full 0/0 byte-id cell — documented, not silent:
  - heap fill (g73_heapfill, COMPILE-only pin): bare /tmp programs
    never link malloc (both stages, pre-existing infra) and ww's
    deref-field READ carries the #24-kin field(SB) leak; the chased
    FILL bytes verified byte-id by hand against the plain (non-alias)
    control — divergence shape identical, all of it in the pre-existing
    read sites.
  - str-field static emit (g73_static_str): the #73 fatal gave way to
    the pre-existing #129 A.2 foldability loud on cs — now both-loud
    twin texts (fill0 class), pinned as K_BUILDERR.
  - struct-copy arm: NO runnable repro reaches these gates — indexed
    whole-struct field reads take the (already chased, byte-id) address
    spine, and tuple-of-struct louds upstream on BOTH stages (#54-kin
    "aggregate init from unhandled rhs shape"). The arm's only carrier
    was the heap gate, covered by the compile pin above.

944_alias_cgen_b5_run grows 16→25 rows (65 checks); all 944-family
suites green; sizelint 0.
2026-06-06 02:19:39 +09:00
1cc663f494 cgen: B5-c1 helper+funnel single peels fold into type_chase_named — 19 sites, cs-only
The exact F2b c1 set (rob next-arc spec + B5 re-rule): node_tuplearg:249,
fld_issigned:409, castsrcprim:501/:531, struct_float_class:598,
tagged_arg_size:640, tagged_memarg_size:661, type_isnullable:740,
nullable_ptr_tag:750, cg_tagged_success_tag:860, cg_variant_is_error:876,
cg_tag_for_variant:899, type_istagged:953, type_unwrap:1269 + the widen/
fill funnel entries cg_widen_tagged_store:2456/:2480/:2483,
cg_widen_tagged_push:2905, cg_structlit_fill:3195. Raw `->under` in
cgen.c 88→69. Riding per re-rule R1: peel-ok-#218 annotations at
cg_variant_match/cg_variant_struct_match (citing ken's b5 oracle §4 —
chasing those four peels graduates zero v2_struct rows; the real fix is
a both-stage NAMED-source arm, task #95) and the :755 peel-ok annotation
mirroring ww cgenutil.ww:2758 (probe-cleared, 018ef66). :447 untouched
(c2's grant).

TRAIN INVARIANT: cs-only — zero selfhost/ or lib/ bytes move; w6c_ww/
ww_ww/w6a_ww/w6l_ww bit-identical to the bcd948d baselines (md5
28ad889042bad8006f1997cbcec94805 / 4e9ca8741f19e1f68219ff799a5e5a14).
cs movers bounded to exactly: kb5_targ, kb5_tmem, kb5_wpush, kb5_null,
kb5_f32p, kb5_fill2, kb5_tuparg_c; five selfhost mains + the rest of the
kb2/kb3/kb4/kb5/kna corpus byte-NEUTRAL both stages.

LIVE graduations: kb5_targ (tagged_arg_size sized a 2-level alias union
param 0 → wrong arg path, cs silent exit 1) and kb5_tmem (>48B memarg
twin) → 0/0 byte-id. Divergence flips to byte-id: wpush/null/f32p.

#85 CLOSES as SITE-CLOSURE with ZERO live graduations: type_unwrap's
two consumers (:14716/:14907, both tuple-global layout walks that want
the chased view) are checker-DEAD on cs for alias tuples (#86 upstream)
— correctness there is by-construction, pinned by tupglobal_bound86.

DESIGNED DIVERGENCE (re-rule R4, task #100): the :3195 chase flips
kb5_fill2 from both-wrong-IDENTICAL-silent (gate-blind, both stages
accepted and ran wrong byte-identically) to cs-LOUD / ww-silent-wrong.
A loud, disclosed, pinned divergence over a silent miscompile; rejected
programs emit no asm so the byte-id gates hold. Dual-cell pin
(fill2_bound100): cs experr + ww run-exit-1 both asserted; fill0
both-loud control holds. #100 (the ww twin gate) fires immediately
after B5 so the window is one train wide.

Oracle corrections at the c1 boundary (ken c1-BOUNDARY ADDENDUM,
verified on his independent scratch build; rob ack'd, scope unchanged):
  C1-CORR-1: kb5_succ does NOT flip here — its residual divergence is
  exactly three paired return-position tag syntheses, the cgreturn
  return-route family (:12278/:12318/:12322). Joins c3's graduation
  set; pinned succ_bound_c3 K_RUN_NOID until then.
  C1-CORR-2 (corrects re-rule R2): kb5_wstore_a does NOT flip — the
  ident-lhs N_ASSIGN tagged store gates in the cgexpr INLINE set (B6),
  never reaching the :2456 funnel; cs byte-neutral here. Pinned
  wstore_a_bound_b6 K_RUN_NOID; byte-id rides B6.
  C1-CORR-3 (corrects re-rule R3 + ken FLAG-2): the :249 chase is NOT
  purely latent — the CAST spelling (kb5_tuparg_c) earned a LIVE cs
  graduation (cs ok/1 → ok/0, correct tuple-arg classify); ww still
  runs wrong (task #99). Two-key pin tuparg_cast_bound99: cs-0 earned +
  ww-1 pinned observed-wrong; byte-id re-pins to full 0/0 when #99's ww
  fix lands.

New 944_alias_cgen_b5_run row table (16 rows, 40 checks): controls
signed/wstore/wstore1 byte-NEUTRAL as predicted (kind-keyed tests are
the only behavior-visible peels — type.c classifiers already recurse);
literal tuparg spellings stay dual-cell bounds (#99/#86). All 944-family
suites green; sizelint 0.
2026-06-06 02:15:14 +09:00
bcd948de88 wcc_ww/check: c4 #80 bare-binder forrange dealiases the iterable's type expr
F2a batch-4 c4. RE-PROBED AFTER c1 per spec: still live at the c3
train base with a REBUILT w6c_ww (the mechanical tichase collapse
didn't cover it — this read is AST-node-keyed, not tinfo-keyed).

REPRO (.ai/scratch/repro_f2a1_b4.ww): `untyped_lit * rangevar` over a
range-for of an alias-typed slice (`type slk = []int`) — wwstage
checker dies "asserttyped: bin" at the binop; cs accepts and runs 0.

TRACE: check.ww resolvewalk N_FORRANGE bare-binder arm — the binder's
element type comes from kind-testing the scrutinee's type expr
(N_TSLICE/N_TARRAY), but an alias-typed iterable arrives as N_TNAME:
both tests miss, the binder falls to the N_FORRANGE fallback decl,
stays untyped, and the first binop over it bails. cs twin types the
binding at scope_define (check.c N_FORRANGE) — accepts.

FIX (single site, the one the repro traces to, per grant): dealias
via the existing resolvealias(unwrapbang(it)) idiom before the kind
tests. The TUPLE-DESTRUCTURE arm carries the same unresolved tests
but is NOT in-grant (fixing the bare arm is not a no-op, so the
re-spelling clause does not apply) — FILED as task #97; currently
double-masked bounded-loud (cs louds upstream at #270-1c so the
alias-tuple-slice iterable is unconstructible on cs; ww asserttyped).

Rows (944): rangevar_alias2 (the repro, cs0/ww-reject -> 0/0 byte-id)
+ rangevar_plain_ctl (non-alias control, held throughout). 944
202/202. Corpus: five-mains ww NEUTRAL vs base on identical inputs
(checker-acceptance-only change; no alias range-for in corpus).
combined.ww regens ride along.
2026-06-06 01:04:19 +09:00
10d886906f wcc_ww/check: c3 spread-member + &len/cap base walks chase the full alias chain
F2a batch-4 c3, the check.ww behavior pair (exact set). Both cs twins
ALREADY full-chase at this SHA — ww-only align-up, no fuse:

B1 (check.ww:2038 census line; peel was at :2041 post-c1): the
`...inner` spread member peeled ONE level, so a 2-level-alias inner
union escaped the TY_TAGGED splice test and rode as a SURFACE member —
the outer box sized off the inner union's own header (probe sp_alias2:
runtime 0/0 BOTH but byte-DIVERGE, ww frame $48 vs $32). cs twin
chases (check.c:755 type_chase_named, the spec's :660-667 cite moved).
Fix: vu = tichase(vu) under the isspread gate, mirroring cs's
`spread ? type_chase_named(vt) : vt`. sp_alias2 graduates 0/0 byte-id;
sp_alias1 1-level control held throughout.

B2 (check.ww:2273/:2275): the &x.len/&x.cap base walk hand-peeled
NAMED->PTR->NAMED one level per hop — 2-level alias bases fell out of
the slice/str detect and took the generic *opt typing. cs twin chases
both hops (check.c:1198-1201). Fix: tichase per hop, exact cs mirror.

B2 ROUTE-TRACE (flagged, not edited): the spec's "post 0/0" cell for
amplen2 is NOT achievable inside this grant — OBSERVED pre-fix that
ww dies LATER at the cgen ADDRESS tail ("unsupported address-of
shape") for ALL alias bases 1+ LEVEL (cs runs 0): task #96 (ken
b4-oracle), a cgenexpr.ww site outside c3's check.ww set. The chase
here aligns the stamped type only (latent until #96); rows pinned
LOUD-HOLD via the new K_RUN_CS_WWERR kind (cs builds+runs; the ww
build must keep FAILING loud with the pinned diagnostic — a silent
ww accept-and-run is the regression the bound row exists to catch),
citing #96; they graduate K_RUN with it. Also OBSERVED: the let-init
spelling (`let s: sl1 = [1,2,3]`) is blocked further upstream by the
rows use the param route.

Pin rows (944): sp_alias1 K_RUN hold, sp_alias2 K_RUN graduation,
amplen_plain K_RUN control hold, amplen1/amplen2/ampcap2
K_RUN_CS_WWERR LOUD-HOLD bound on #96 (ww leg asserts "unsupported
address-of shape"). 944: 196/196.

Corpus: five-mains NEUTRAL vs base on identical inputs, cs==ww holds
(both clusters zero-in-corpus). combined.ww regens ride along.
2026-06-06 01:03:59 +09:00
4e174099bd cgen: c2 variant pass-2 structural fallback chases the full NAMED chain — both stages fused
F2a batch-4 c2. Site: cgenutil.ww flatvariantidxt pass-2 (was :2895
at 74195ac, :2903 at 4adf914 post-batch-3) + cs twin cg_tag_for_variant
(cmd/w6c/cgen.c:920-933).

The structural fallback matched a bare source against a NAMED variant
by peeling exactly ONE level (pu.under compare) — a 2-level-alias
variant (type a=*X; type b=a) missed every pass and the widen
defaulted to tag 0, SILENT (the legacy-#17-comment class; that
comment's "task #17" label is retired here — current task #17 is the
unrelated arrlit item).

cs-twin probe DECIDED THE FUSE (spec obligation): v2_alias2 (bare
*i64 into 2-level ptr-alias variant) ran exit 1/1 BOTH-WRONG-IDENTICAL
byte-id pre-fix — cs has the identical single peel, so both stages fix
in this commit (NOT ww-only align-up). NOTE: the cs BINARY is NOT
frozen this train — this commit legitimately moves cstage codegen;
movers must stay bounded to the c2 family (verdict-sweep obligation).
Fix: typeeq/type_eq against tichase/type_chase_named of the variant;
the TY_NAMED gate keeps bare variants in pass-1's exact domain; drew's
>=2-candidate hard-error now guards the CHASED match set (v2_ambig
pins it HOLDING; v2_ambig2 pins the RATIFIED acceptance NARROWING —
2-level twins flip build-accepted-silent-mis-tag -> hard-error BOTH
stages, FLAG-P1/kb4_v2_ambig2). Not nominal-sensitive beyond the
documented proviso — the chase only deepens the structural compare;
nominal choice among >=2 candidates still hard-errors (#209/#211 hold
condition does not trip).

ROUTE-TRACE (rob's §3 ruling) — outcome (b): post-c2 the c4_bool2
shape (2-level bool alias variant, concrete-bool source on ww) runs
0/0 fully BYTE-ID — ww's concrete bool (the #90 stamp divergence,
still open) now reaches the variant through the chased structural
fallback and converges with cs's untyped-funnel route on the same
tag. The graduation therefore belongs to THIS commit: 944
untyped_bool2lvl_bound90 flips K_RUN_CS -> K_RUN here; c5 (#90 stamp
flip) pins its own acceptance rows.

Pin rows (944, all OBSERVED at the c1 base):
  v2_ctrl    bare *T into 1-level NAMED-*T (io vtable shape)
             pre 0/0 byte-id -> post HOLDS (the #15 consumer);
  v2_alias2  bare *T into 2-level alias variant
             pre cs1/ww1 byte-id (both-wrong) -> post 0/0 byte-id;
  v2_struct2 bare anonymous-let struct into 2-level alias variant
             pre cs1/ww1 -> post 0/0 runtime; byte-id SKIPPED via new
             K_RUN_NOID row kind: asm diverges on 3 PRE-EXISTING cglet
             zero-fill lines (cs XORQ+2 stores, ww none — #81 class,
             runtime-correct both, orthogonal to the tag; observed
             identical pre/post). Flip to K_RUN when that closes. The
             adjacent NAMED-source shape is task #95 (ken b4-oracle),
             OUT of this set;
  v2_ambig   bare source matching >=2 NAMED variants (1-level twins)
             -> hard-error HOLDS both stages (K_BUILDERR, diag pinned);
  v2_ambig2  2-LEVEL twin variants — the RATIFIED narrowing pin:
             pre build-ACCEPTED both stages (chase-less fallback
             matched neither twin; silent mis-tag, byte-identical)
             -> post HARD-ERROR both stages (K_BUILDERR, diag pinned).

Corpus: five-mains NEUTRAL vs the c1 build on identical inputs (both
stages — the 2-level variant shape is zero-in-corpus, as the old
comment predicted); cs==ww byte-id holds; 944 181/181.
combined.ww regens ride along.
2026-06-06 01:03:22 +09:00
b26b3dbdc5 wcc_ww: c1 collapse plain TY_NAMED chase loops into tichase across cgenstmt/cgen/check — byte-id neutral
F2a batch-4 c1, mechanical, per rob's spec census (re-verified at
4adf914; check.ww/cgenstmt.ww cites were stable as predicted, the
two cgenutil [B3-SHIFT] cites moved +8: :2746->:2754, :2895->:2903).

42 conversions, every site eyes-classified plain nil+NAMED:
- cgenstmt.ww 26: one-line :149 :325 :352 :532 :551 :594 :715 :732
  :938 :940 :1577 :1629 :1782 :2230 :2575 :3372 :3678 + multi-line
  :986 :1912 :2287 :2291 :2702 :2759 :2823 :3037 :3715 (all plain,
  no extra guards).
- cgen.ww 13: one-line :1505 :1541 :1658 :1691 :1697 :1947 :1951
  :2065 :2109 :2118 + multi-line :2239 :2648 :2663 (incl. the
  emitdefconstants struct/array arms — plain).
- check.ww 3: :1627 tupleelemslot, :1673 fieldslotsize, :4111 arrlit
  elem chase; WHY comments kept. tichase visibility verified: same
  `package wcc` (cgenstmt/cgen already call it) — no second helper.

Survivors (classified, untouched):
- cgenutil.ww :1301 tichase's own body (the accessor) + :2754
  nullableptrtag single peel (batch-2 c3-B2 PROBE-CLEARED, 018ef66 —
  peel-ok annotation added this commit, cites the banked record; cs
  twin cmd/w6c/cgen.c:747 verified at this SHA) + :2903
  flatvariantidxt pass-2 guard (batch-4 c2's site).
- check.ww :1765 comment + :1805 construction write (peellint-ok
  annotation added — the one WRITE building the NAMED link) + :2038
  spread peel (c3-B1) + :2273/:2275 &len/cap hand-walk (c3-B2) +
  :3666 comment FALSE POSITIVE ("io.underread" matches `.under`).
- cgenexpr.ww :2886 comment only.

Lint note for the peellint finale: the `.under` token-match must be
token-bounded — check.ww:3666's "io.underread" is a comment
false-positive at a non-boundary match.

Behavior sites (:2038, :2273/:2275, :2903) and #80's site excluded
per spec — they ride c2-c4.

Byte-neutral proof: rebuilt w6c_ww on the five BASE main.combined.ww
inputs -> all five .s byte-identical to the base build's outputs;
cs==ww byte-id holds on the five REGENERATED combineds; test-unit
290 green. w6c_ww + wwdump combined.ww regens ride along.
2026-06-05 23:58:54 +09:00
4adf914f2f wcc_ww/cgen: #92 rhsstructpayload N_STRUCTLIT arm through structlookupchain
The N_STRUCTLIT arm still did bare structlookup on the literal's type
name — an alias-named struct LITERAL widened into a union (`type ali =
base; let v: (void|ali) = ali{...}`) missed the registered structinfo
and fell to the scalar widen arm: word0-only/dropped payload, cs 0 /
ww EXIT 1, byte-id NO (reviewer-B2 find, pre-existing at 486f7f8's
parent). Exactly the class batch-2 c2 closed for the N_IDENT-local arm
of the SAME function; same funnel fix — route through structlookupchain
(cgenutil.ww:1758) and return si.sname so every consumer's re-lookup
hits the REGISTERED name.

One pre-authorized rider: structlookupchain's entry gate accepted only
N_TNAME, but a struct literal's type ref parses as N_IDENT (expression
position, lib/ww/parse/expr.ww builds s.lhs as N_IDENT — OBSERVED).
Widened to `N_TNAME || N_IDENT`; the entry-kind predicate is spelled
twice (the early-return and the chain loop's first iteration — the
loop reassigns cur to aliaslookup results, which are always N_TNAME,
so later iterations are untouched). Consumer census, all 10 call
sites: cgenutil:3187(+this arm; post-commit position), cgenexpr:2927/3006/8775/9011,
cgenstmt:3033 guard on N_TNAME explicitly before calling;
cgenexpr:5756 (etnode = type-spec .lhs), cgenstmt:1978 (arrtn.lhs or
the synthesized #79 N_TNAME), cgenstmt:2695 (cglet n.lhs declared
type spec) pass parsetype products only — parsetype builds zero
N_IDENT nodes (lib/ww/parse/parse.ww). No existing caller can pass
N_IDENT: the widening is strictly accepting-more; existing callers
unmoved. NO second inline chase in the arm (close-by-construction).
Base spellings byte-id: structlookup hits at the chain entry and
si.sname == the literal's own name — same string out, same asm.

Pin: 944_alias_accept_run union_slit_{base_ctl,alias,alias2,order} —
base control holds 0/0; 1/2-level alias + permuted decl order graduate
cs0/ww1-byte-id-NO -> 0/0 byte-id. #63 PROBED post-c3 per the
enrollment rule: does NOT green at this site (8B alias struct-lit
let-init still ww silent-zero-fill exit 1; 16B still loud "aggregate
init from unhandled rhs shape") — its miss is the cgenstmt let-init
dispatch, not rhsstructpayload; documented on task #63, left red, no
row enrolled.

Light gates: test-unit 290 green; sizelint 0; 989 ratchet zero flips;
five-mains NEUTRAL vs master-74195ac scratch on identical inputs +
cs==ww on all five. combined.ww regens ride along (#110).
2026-06-05 23:38:54 +09:00
3e9a6955e7 wcc_ww/cgen: #88 defisaddressable array leg chases the stamped def type
The `&D` addressability gate (defisaddressable, cgen.ww) keyed its
array leg on the UNCHASED syntactic dtnode (N_TARRAY) — a def whose
declared type is an ALIAS of an array missed the gate and fell to the
rule-7 loud error, but the gate was lying: the ww def-array DATA
emitter (emitdefconstants' array arm) already peels TY_NAMED off
d.lhs.type_ transitively, so the alias def HAS a DATA symbol
(probe-OBSERVED: `DATA main.D(SB)` emitted byte-id by both stages for
the &-less program). Gate-only fix — tichase(dtn.type_) == TY_ARRAY —
restores gate == emission set exactly; no emitter twin, no half-state.
The struct leg (defvarstructinfo) already chased; plain [N]T defs
agree under tnode and chased reads, so existing rows are
byte-id-neutral by construction. cstage gates TK_AMP on the
def_isarraydef registry fed by the g-fold-G1 chased let_isarray
(cgen.c:3975-3977, 1446) and runs every row 0 — align ww UP.

Pin: 944_alias_def_addr_run, 6 rows (plain + struct-def controls
hold 0/0; 1/2-level alias + fwd-ref decl order graduate ww-LOUD ->
0/0 byte-id; str-def &S error-path STAYS LOUD both stages with a
byte-identical diagnostic — the rule-7 tail text is compared w6c vs
w6c_ww, so a silent reject or a divergent message both fail the row).
def_l2's readback casts to the base array ptr: the natural (*p)[2]
spelling over a 2-LEVEL-alias pointee trips a SEPARATE pre-existing
CSTAGE double-deref (spurious MOVQ (AX),AX, SEGV; def-independent, ww
correct) — filed as task #93 (#85 type_unwrap kin, F2b OUT), not
fixed here (site-set form).

Light gates: test-unit 290 green; sizelint 0; 989 ratchet zero flips;
five-mains NEUTRAL vs master-74195ac scratch on identical inputs +
cs==ww on all five. combined.ww regens ride along (#110).
2026-06-05 23:38:41 +09:00
d5cb1bd69e wcc_ww/cgen: #82 cgun &base[i] classify off the chased stamped base type
The TK_AMP N_INDEX arm keyed arrayness off the SYNTACTIC tnode (local
leg isarr at the baselocal read; global leg isglobalarr/isglobalptr at
the letvartnode read) — an alias-typed base (tnode N_TNAME) missed the
N_TARRAY gate, so the base materialized as MOVQ (element-0 VALUE)
instead of LEAQ (storage address): wild pointer, SEGV/corruption on
the deref. SILENT class (metric-1). The global leg graduated from
latent to live when g-fold #77/#78 landed alias-global DATA emit.

Fix re-keys both legs off tichase(base.type_) gated on TY_NAMED — the
landed cgindex #60 idiom (cgenexpr.ww:1800-1820). cstage already
classifies off the chased type (type_chase_named, cmd/w6c/cgen.c:
4172-4188) and is the runtime-correct reference: align ww UP. esz does
NOT move — elemsizeofc chases internally since batch-2 (PREMISE-2
probe-confirmed via amp_narrow: stride right, base wrong pre-fix).
Non-alias rows byte-id-neutral by construction (TY_NAMED gate).

Pin: 944_alias_amp_idx_run, 8 rows through the taken pointer (plain
local/global+str controls hold 0/0; 1/2-level alias local + global,
fwd-ref decl order, narrow [4]u32 graduate cs0/wwSEGV-byte-id-NO ->
0/0 byte-id). Probed OUT, filed not fixed (spec §1 NOTE-2): &D[i]
def-array base breaks at a DIFFERENT site both stages (cs XORQ BX,BX
zero-base cgen.c:4209-4212, ww complex-base fallback; both SEGV 139).

Light gates: test-unit 289 green; sizelint 0; 989 ratchet zero flips
(31 ID / 9 DIVERGE / 3 WWREJECT pins hold); five-mains NEUTRAL vs
master-74195ac scratch build on identical inputs + cs==ww on all
five. combined.ww regens ride along (#110).
2026-06-05 23:06:46 +09:00
74195ac4dc wcc_ww/cgen: c4 tyassignableuntyped full-chase — structural alignment, zero acceptance graduations, graduations ride #90
F2a batch-2 c4, per rob's FINAL RULING (i) (banked, .ai/rob-batch2-spec.md):
ww-only — the cs twin (cmd/wcc/type.c:369-385) is already harec-shaped
since F1 9bd0d8b. Port of ref/harec/src/types.c:989-996
type_is_assignable structure: full dealias of the dst when its chased
kind is not tagged (du = tichase(dst); the STR/BOOL/NIL arms read du);
the tagged variant drill keeps the ORIGINAL p.type_ and detects a
variant's taggedness via the full chase (was one-level — a 2-level
alias-tagged variant slipped INTO the recursion); INT/FLOAT/RUNE keep
the recursive predicates on the unpeeled dst, exactly as cs.

STRUCTURAL ALIGNMENT, ZERO ACCEPTANCE GRADUATIONS, GRADUATIONS RIDE
#90: on wwstage no bool/str literal reaches this funnel today — the
checker stamps N_TRUE/N_FALSE CONCRETE bool (check.ww:2440) where cs
stamps ty_untyped_bool (check.c:1234), the bool member of the #14
N_STRLIT stamp cluster (task #90, filed this train; its metadata
carries the graduation obligations: BOTH layers, the stamp AND the
flatvariantidxt pass-2 one-level pu.under guard at :2868/#17, must
clear before the bound rows flip). The live silent-wrong this would
close — `type bb2 = bb1 = bool; let v: (void|bb2) = true; v is bb2`
runs cs 0 / ww 1, single-line asm diff (tag store MOVQ $1 vs $0) —
therefore does NOT graduate here: the row dies one layer up
(pins-follow-the-layer).

test 944 (+7 rows): untyped_hold33_pin ((void|T) untyped-int init,
the #33 tagged-dst drill HOLD) + untyped_tagdst_2lvl_hold (2-level
alias tagged dst) + untyped_str2lvl_ctl (masked twin — the shape
fallback rescues str, documents the rescue) + untyped_int2lvl_ctl +
untyped_bool1lvl_ctl all K_RUN 0/0; untyped_bool2lvl_bound90
K_RUN_CS BOUND citing #90; untyped_nestvar_bound K_BUILDERR_CS BOUND
expected-state row (#199-alpha + #90) — untyped literal into a
NESTED-tagged alias-wrapped variant: cs CHECKER loud-rejects
("not assignable", the #199-alpha ww-stricter no-transitive-drill
rule, type.c:316-324) while ww's node-keyed checker accepts and the
binary runs 0 OBSERVED post-chase; the acceptance gate is check.ww
(batch-4 aligns ww down; the row flips to loud-BOTH then; ww half
pinned via #90 metadata — the harness has no cs-reject+ww-runs
mode). Every row's cs column OBSERVED (built+run) at dispatch-SHA cs
per the ratified condition; probes at /tmp/b2/c4.

989 lib-byteid ratchet: ZERO flips (31 ID / 9 DIVERGE / 3 WWREJECT
hold exactly as pinned). Byte-id: five-mains NEUTRAL on identical
inputs vs the c3 build; cs==ww on the regenerated five; test-unit 288
green. combined.ww regens ride along.
2026-06-05 22:36:55 +09:00
018ef6680e wcc_ww/cgen: c3 in-file peel sites — exprprimresolved base walk chases (B1); elemsizeofc alias-indexable leg (B3); B2 probe-cleared
F2a batch-2 c3, probe-decided per rob's spec.

B1 (exprprimresolved N_DOT, was :2073/:2075/:2078): the hand-rolled
NAMED->PTR->NAMED->STRUCT walk ran out at a 3-level alias base or a
ptr-to-2-level base — the cast-source width stayed unknowable on
wwstage only, so the #33 identity clamp was emitted where cstage
skipped it (runtime-correct both, byte-id NO, gate-blind). cs twin
castsrcprim is F1-chased at both hops (type_chase_named) -> ww-only
align-up, no fuse. Probes: b1b_3lvl + b1b_ptr2lvl graduate 0/0
byte-id; 1/2-level controls held throughout (the 2-peel covered
2 levels by accident); the FIELD-u chase is asm-neutral (cs keeps a
single peel there, sound via type_isint NAMED recursion + NAMED size
carrying the underlying's — probe b1c_fld2lvl byte-id). 944 rows
castprim_3lvl_base / castprim_ptr2lvl_base / castprim_2lvl_base_ctl.

B2 (nullableptrtag variant scan :2719): PROBE-CLEARED, NO FIX. The
2-level ptr-alias nullable variant (b|void), the order-swapped
(void|b) that defeats the return-0 fallback, AND 3-level all run 0/0
byte-id both stages — the constructible variant params never carry a
2+-level NAMED at this scan. cs twin nullable_ptr_tag (cgen.c:747)
has the identical single peel: parity, both correct on every
constructible shape. NOT nominal-sensitive (shape-keyed first-PTR
scan, no name choice exists) — the spec's #209/#211 hold condition
does not trip. The single-peel if stays, classified survivor.

B3 (elemsizeofc): an alias-NAMED INDEXABLE (`type grid = [3]cell`)
arrives as a bare N_TNAME — elemsizeof's name arm knows only
str/prims and answered the 1-sentinel through the `direct != 8`
short-circuit (#60 esz-1 family, outer-array leg). New N_TNAME leg
answers from the chased stamped tinfo via idxeffti. #83 caller
probes (rule: no unprobed caller row):
- append leg: UNREACHABLE on ww — `let xs: nums = [];` rejected by
  the wwstage CHECKER ("let: not assignable") even at 1 level; cs
  accepts+runs. check.ww is batch-4 scope -> documented on #83.
- alloc-empty leg: BOTH stages loud-reject the alias-typed binding
  hint ("cannot infer slice element type", check.ww:2547 + cs twin)
  — symmetric loud, alias-blind hint documented on #83.
- arr[i].field= float leg: plain [3]cell runs 0/0 byte-id; the
  alias-named outer array LOUDS on ww at its own site
  (cgenexpr.ww:11033 assign-resolver float-field bound), cs runs —
  fails upstream of elemsizeofc, documented on #83.
- subslice-of-alias-array call-arg probe: already 0/0 pre-change
  (the #60/#257 stamped-tinfo keying answers first) — the internal
  chase is presently LATENT defense-in-depth; ZERO callers enroll.
Remaining elemsizeofc internal loops (eti/esub) fold to tichase.

Byte-id: five-mains NEUTRAL vs the c2 build on identical inputs;
cs==ww on the regenerated five; 944 138/138; test-unit 288 green.
combined.ww regens ride along.
2026-06-05 21:55:51 +09:00
f7fd3e4850 wcc_ww/cgen: #62 Layer-2 (ww half) — rhsstructpayload chases the alias chain; widen-store su at entry
The wwstage tagged widen's struct-payload classify was name-keyed:
rhsstructpayload's N_IDENT arm did a bare structlookup on the local's
declared TNAME, so an alias name (ali->base) missed the registered
structinfo and the value fell past the struct arm to the SCALAR widen
arm — word0-only box payload, words 1+ zero-filled. At normal decl
order this was BOTH-WRONG-IDENTICAL with cstage pre-F1 (byte-id YES,
gate-blind; F0 m5b_match1 exit 2/2). Choke-point fix: route the arm
through structlookupchain (the name-domain twin of cstage's
su = type_chase_named, c138605) and return the REGISTERED struct
name so every consumer's re-lookup hits. All three consumers
graduate through the one funnel (proof-grep: rhsstructpayload is the
only struct-payload classifier): cgwidentaggedstorebp's struct arm
(let/assign/match), pushargsrev's widen route (the call-arg twin),
and cgreturn's needswiden (cgenstmt.ww:1022). The variant TAG still
keys on the un-chased stamped type — the member's nominal identity
is the alias (cg_tag_for_variant precedent).

Rider, same store family: cgwidentaggedstorebp now chases the source
classify ONCE at entry (post cast peels, where src is final) —
`su = tichase(src.type_)` replaces the five per-arm chases
(nested-arm su / memread su37 / >32B+cast-guard sf37 / tuple-guard
stu72 / tuple-arm stu, all reading the same src.type_), mirroring
cstage cg_widen_tagged_store's su position. dt entry loop + the
remaining in-function plain chases (castu/ceti/fti/fvt) collapse to
tichase — mechanical, excluded from c1 only because the function was
c2 scope wholesale.

Runtime graduation (banked rows .ai/impl62r_layer2_rows.md):
m5b_match1 (L2-1 norm) + m5_match (L2-2 fwd) cs0/ww2 -> 0/0 byte-id;
L2-4 3-word (last payload word) 0/0; push-twin arg row 0/0; control
m5b_match0 holds 0/0 (ken confirms at verdict). test 944: the four
K_RUN_CS rows flip to K_RUN (both drivers + byte-id), 129/129.

SURFACED, FILED NOT FIXED (task #89): the RETURN-position leg
(`return x` of an alias struct into a (void|ali) fn return) was
both-wrong-identical (cs1/ww1 byte-id) and is now transiently
cs1/ww0 byte-id NO — ww is the runtime-correct side; cstage
cgreturn's own route into the widen misses the alias (F1 c138605
covered store+push only). F1-precedent transient divergence, noted
per the L2 rows file's acceptance clause.

Selfhost byte-id: new w6c_ww on the c1-HEAD five main.combined.ww —
byte-identical to the c1 build's output (the fix is asm-neutral on
the corpus); cs==ww on the regenerated five. make test-unit green
(288). Closes task #62's ww half — fix-landed-pending-gate (#71
precedent). combined.ww regens ride along.
2026-06-05 21:46:36 +09:00
f19d0cb14d wcc_ww/cgen: collapse cgenutil.ww's plain TY_NAMED chase loops into tichase — byte-id neutral
Mechanical sweep, F2a batch 2 commit 1 (alias arc #5, rob batch-2 spec
c1; same conversion rule as batch-1 ef93b16). Every loop matching the
exact plain shape

    for (X != nil && X.kind == tykind.TY_NAMED) { X = X.under; };

(modulo variable name and one-line vs three-line layout, body exactly
the peel, guard exactly nil+NAMED) becomes `X = tichase(X);`. Census
of cgenutil.ww at 486f7f8: 63 raw `.under` lines, eyes-classified;
38 plain-shape loops converted.

Survivor enumeration (raw `.under` lines left, by post-sweep line):
- 1297 comment text only — not a read.
- 1301 tichase's own body — the accessor itself.
- 1531/1553 elemsizeofc internals — c3/B3 behavior scope (task #83),
  excluded from c1 per spec.
- 2073/2075/2078 exprprimresolved N_DOT single-peel ifs — c3/B1.
- 2719 nullableptrtag variant-scan single-peel if — c3/B2.
- 2868 flatvariantidxt structural-fallback guard (`pu.under != nil &&
  typeeq(pu.under, want)`) — one-level BY DESIGN pending task #17;
  not the plain shape.
- 2895/2904/2923-2924/2934-2935 tyassignableuntyped one-level
  unwraps — c4 acceptance scope.
- 3415/3471/3526/3674/3731/3783/3796/3861/4083/4089 inside
  cgwidentaggedstore(bp) — c2 L2-twin scope, excluded wholesale.

Byte-id evidence: post-sweep w6c_ww vs pre-sweep (486f7f8 build) on
the five HEAD main.combined.ww — byte-identical .s on every input;
cs w6c vs new w6c_ww on the regenerated combined.ww — byte-identical
all five; make test-unit green (288). combined.ww regens ride along
(cgenutil.ww embeds in w6c + wwdump).
2026-06-05 21:40:27 +09:00
486f7f87f9 wcc_ww/cgen: #77 alias-NAMED global ARRAY emit — tichase at the dispatch entry (g-fold G2)
ww half of the #77+#78 fused g-fold train; completes the family. cs
half landed as the previous commit (G1) — the two ship together, one
gated train, per the fuse ruling on both tasks.

Root: the global DATA emit walk dispatched on the UNCHASED decl tnode —
a NO-PEEL consumer (zero `.under` tokens on the path; it never learned
aliases exist). An alias-typed global array's N_TNAME matched no arm
and the documented skip-policy ate the decl: w6c_ww referenced
main.g(SB) but emitted zero DATAW → loud `w6l: undefined reference to
main.g` on every direct alias-global array row (ken NEW-1, all k_gidx*
shapes). Every other kind was already chased (letvarisstr/isslice/
isfloat/isstruct walk aliaslookup chains; the tuple gate walks tnodes;
emitarraydata/emitslicedata chase tinfo internally; letemitsize walks —
registration was never the gap), probe-confirmed: only array rows
failed ww-side.

Fix: ONE tichase at the dispatch entry, per the spec's entry-point rule
— not per-arm. Dispatch arms touched (enumerated):
  emitletdataw (cgen.ww): hoisted `dti = tichase(d.lhs.type_)` at the
    per-decl entry; the isarr8 scalar-shortcut gate and the array arm
    now key on dti.kind == TY_ARRAY (were d.lhs.kind == N_TARRAY) and
    emitarraydata receives dti; the struct zero-fill arm's inline
    TY_NAMED loop collapses into the same dti (ef93b16 precedent,
    byte-neutral). str/float/struct/slice/tuple gates unchanged.
  letpreintern (cgen.ww): the #18 [N]str array-leg gate keyed on the
    N_TARRAY tnode while its body already chased the tinfo — gate now
    keys on the chased kind, so alias-typed [N]str globals pre-intern
    their _S_ labels in decl order (label-order parity with cstage;
    the inner elem chase collapses into tichase).
For non-alias decls tichase is identity (same tinfo pointer) — the
emitted bytes are unchanged by construction; full byte-id invariant
holds (test-unit 288/288 incl. the new table).

main.combined.ww (w6c + wwdump) regenerated by `make` — diff verified
content-identical to the cgen.ww hunks, nothing else.

Graduation table committed as test/wcc/944_alias_global_decl_run.c —
23 rows x {cs run, ww run, byte-id} = 69 checks green. This table IS
the permanent guard: the path is lint-invisible (NO-PEEL — nothing for
the future peellint to see), so only a runtime+byte-id row pins it.
Rows: plain control; alias array 1-lvl read/write/decl-order; 2-lvl
read + order-permuted write (the #78 silent saved-BP rows); [4]u32
narrow-esz; scalar/str/f64/f32 2-lvl; alias-of-named-struct field w/r
(the cs SEGV-at-one-user-level row) + 3-layer + STRUCTLIT init; slice
2-lvl literal; [2]str 1-lvl/2-lvl (letpreintern label leg); def-side
2-lvl array/struct/float; no-regression holds (alias global SLICE,
alias ELEMENT [2]row). Values >255, LAST element asserted.

Probe-OUT rows documented in the test header, filed not pinned: #86
(named-tuple global init: cs checker loud-reject vs ww accept), #87
(plain tagged global: cs silent-wrong vs ww loud-reject, non-alias).
2026-06-05 21:10:35 +09:00