Commit Graph

811 Commits

Author SHA1 Message Date
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
00f71b9781 w6c/cgen: #78 alias-NAMED global decls — let_* + DATA-emit entry chase (g-fold G1)
cs half of the #77+#78 fused g-fold train (rob spec .ai/rob-gfold-spec.md
+ ENROLLMENT RULING 2026-06-05). NEITHER COMMIT FFs ALONE — G2 (wwstage
emit dispatch, #77) completes the train; until G2 lands, ww alias-global
ARRAY rows remain loud link-ERR by design (documented below).

Root: the let_* helper family was single-peel (`u = (t->kind==TY_NAMED)
? t->under : t`) — a 2-level alias chain (or ONE user alias over a named
struct) left u TY_NAMED, so let_collect never registered the global, no
DATA was emitted, and the let_islet-gated load paths fell through to the
frame-local path at offset 0: a silent saved-BP read (probe-verified: cs
emitted zero DATAW and zero main.g references for a2/st1/t2/sl2).

Converted to type_chase_named (6 helpers, per the enrollment ruling —
probes forced let_isstr/let_isslice in beyond the spec's enumerated 4;
non-severable, ruling banked in the spec file):
  let_emit_size  (:1164)  consumers :1408 let_collect gate, :14889
                 emit_lets, :15196 let_pre_intern str-leg — all
                 top-level d->type
  let_isstr      (:1208)  consumer :3894 N_IDENT global load gate
  let_isslice    (:1218)  consumers :3894, :14987 emit_lets slice arm,
                 :15080 emit_defs loud-stop
  let_isstruct   (:1229)  consumers :1432 def registry, :14923 8B-scalar
                 short-circuit gate, :14996/:15062 struct emit arms
  let_isarray    (:1240)  consumers :1445 def registry, :14923,
                 :14975/:14997/:15071 array emit arms
  let_isfloat    (:1251)  consumers :1507 def addressability, :3908
                 N_IDENT float load (non-local branch only — locals take
                 the off!=0 branch at :3793), :14891/:15052 float emit
All consumers sit on top-level-decl or non-local-ident paths; no local
consumer exists. Corpus census: zero >=2-NAMED-layer global decl types
anywhere in lib/selfhost/cmd (all named globals are depth-1: io.vtable,
memio.stream, errno, duration, floatinfo, encoding, ...) — conversion is
identity on the whole existing-green corpus; full byte-id invariant
holds (test-unit 287/287, sizelint clean).

Condition-3 members (ruling: "own inline peel on the routed path = same
family, enroll if it fixes at the same chase" — verified: every enrolled
probe row graduates at this chase, none elsewhere): the routed-to DATA
emitters re-peeled at entry and return-0'd into the silent skip path.
Converted the OUTER-type entry resolution only:
  emit_struct_lit_bytes :14238, emit_struct_data :14363,
  emit_array_lit_bytes :14401, emit_strarray_data :14618,
  emit_array_data :14791, emit_slice_data :14830,
  let_pre_intern array-leg :15131
ELEMENT-type peels in those helpers are untouched (different axis, out
of this fold). type_unwrap itself is NOT converted (#85, explicit OUT);
its two remaining consumers (:14711/:14902) are tuple-arm-only, behind
the checker reject filed as #86.

Probe matrix (banked /tmp/implG_probes.md + /tmp/implG/): 34 rows, both
stages. Post-G1: every cs alias-global row runs 0 — a2/a2o (the #78
silent saved-BP rows), st1/st2 (silent SEGV at one user alias level),
stlit/t2/sl2/d_a2/d_st2/tsa2 (silent-wrong), s2/s2o/f2/f2s/d_f2 (loud),
u1/tsa1/a1* (held green). All ww-green rows byte-id YES. ww array rows
stay loud link-ERR until G2 (`w6l: undefined reference to main.g`).
Controls + holds (plain globals, alias-ELEMENT el1, alias-slice sl1)
unchanged. OUT, filed: #86 (named-tuple global init, checker), #87
(plain tagged global, cs silent vs ww loud — not alias-family).
2026-06-05 20:45:49 +09:00
ef93b1637e wcc_ww/cgen: collapse cgenexpr.ww's plain TY_NAMED chase loops into tichase — byte-id neutral
Mechanical sweep, F2a batch 1 commit 2 (alias arc #5, rob F2 ruling:
one chased accessor is the only spelled way to dealias; this
enumeration seeds the future peellint whitelist). 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
cgenexpr.ww at commit 1: 71 `.under` lines — 49 one-line peels + 21
multi-line-spelled peels, all eyes-classified as the plain shape, all
70 converted (count-asserted).

Survivor enumeration (raw `.under` occurrences left in cgenexpr.ww):
- cgenexpr.ww:2886 — comment text only ("follows the resolved
  NAMED.under pointer"), no code read. Classification: not a read.
Zero code-level raw `.under` reads remain in this file.

Byte-id evidence: pre-sweep w6c_ww (commit-1 build) vs post-sweep
w6c_ww compared over a 31-input corpus — all selfhost/cmd/*/
main.combined.ww (the compiler's own full source) plus the #60/#79
probe set — identical exit codes and byte-identical .s for every
input. make test-unit green (287). combined.ww regens ride along
(cgenexpr.ww is an embedded source).
2026-06-05 20:14:49 +09:00
da81a4c86e wcc_ww/cgen: #60+#79 alias-NAMED array/slice ELEMENT paths read the chased tinfo — tichase lands, SEGV families graduate byte-id
One class: alias-blind base+esz at the array/slice ELEMENT paths —
index read/write, slice-expr, for-range, and literal-init store. The
wwstage cgen derived element size and base addressing from the
type-AST tnode; an alias-typed base (`type arr = [4]int; let a: arr`)
shows only the N_TNAME leaf, so esz fell to a sentinel (1 on the read
side, 8 on the init-store side) and the base classified as a POINTER
(MOVQ of array words, no IMULQ): m8b_idx1/range1 SEGV 139, m8b_slice1
silent-wrong past little-endian prefix-luck (m8c_slice1big exit 2),
m7c global [2]row read SEGV via the alias-blind element-is-array
classify, and (#79, ken F2a1 oracle) `type A=[4]u32; let a:A=[...]`
stored MOVQ stride-8 over a stride-4 slot — elements 2/3 landed at
0(BP)/+8(BP), a saved-BP/RIP smash masked whenever esz==8. cstage
reads everything off the chased stamped type (type_chase_named/
idx_eff, correct post-F1), so every fixed shape graduates
ww-SEGV/silent-wrong -> 0/0 byte-id.

New tichase() in cgenutil.ww: nil-passthrough transitive TY_NAMED
peel, exact twin of cmd/wcc/type.c:160-162. Routed sites, all gated on
the stamped type being TY_NAMED (non-alias paths byte-identical):

- cgindex (cgenexpr.ww): elem facts (esz/signed/float/f32) off
  tichase(n.type_); etn falls back to n for the tagged/str/slice
  classify; LEAQ-vs-MOVQ base off the chased kind; elem-is-array
  supplemented by tinfoisarray(n.type_) for alias ELEMENTS (m7c).
- cgassign N_INDEX store + compound arms (cgenexpr.ww): esz +
  elemtn=lhs (the stamped-element idiom of the N_DOT/N_INDEX arms);
  chased-kind base classify at all four LEAQ/MOVQ sites.
- cgslice + cgbasecap (cgenexpr.ww): esz, base classify, default-hi
  (TY_ARRAY -> $alen / TY_SLICE|TY_STR -> +8 len), cap word at +16;
  global-str cap keeps the #73 carve-out.
- cgforrange (cgenstmt.ww, cross-file leg: the range pin cannot green
  without it): esz, element-node synthesis off .sub (FC0 precedent),
  isarr/isslicestr classify, alen off the chased tinfo.
- cgarrlitfillbp (cgenstmt.ww, #79): an alias [count]T arrtn is the
  N_TNAME leaf (elemn nil) — synthesise the element node off the
  chased sub so the existing prim/agg/slice/tagged/narrow dispatch
  works unchanged; `...` repeat bound off the chased alen (cstage
  cg_arrlit_fill_bp receives the pre-chased bu and reads bu->alen).
  #8-PAIR COVERAGE: this is the STORE half of #8's two size-sources.
  The elemsizeofc READ half chases the ELEMENT internally (idxeffti +
  esub peel, the #8 fix) but NOT an alias-typed INDEXABLE node — that
  leg is covered at its #60-family call sites by the gates above
  (cgindex/cgslice/store/compound/cgforrange/pusharg). Remaining
  alias-blind elemsizeofc callers are enumerated as residuals below.
- bare-let classify (cgenstmt.ww, #79 rider): `let a: arrk;` with an
  alias-to-array type took the composite zero-fill cstage doesn't
  emit (cstage keys the no-init shape on the chased lu->kind: arrays
  keep the per-index-write contract; an 8B alias-array still falls to
  the single MOVQ $0 arm). Required for the loopfill_1024 pin's
  byte-id; closes the array kind of the uninit-alias divergence.
- pusharg N_SLICE (cgenutil.ww, pulled in by the same pin rule: the
  944 slice_of_alias_arg row is a distinct lowering from cgslice):
  esz, base classify, default-hi.

Tests: new 944_alias_idx_family_run (19 rows: idx/slice/range/init
controls + 1-level + 2-level + decl-order permutations + index store
+ compound (+=, *=) + #79 [4]u32 literal-init + alias `[v...]` repeat
+ uninit [1024] loop-fill + slice1big (1000 elems, values >255,
LAST-element readback, default-hi, .cap, range count) + re-slice of an
alias slice + range over an alias slice + m7c global 2D + GLOBAL
alias-slice indexed read + slice-as-call-arg; dual-stage run +
per-row byte-id; LAST elements asserted throughout). The six
944_alias_accept_run rows citing "#60 (F2 batch 1)" flip K_RUN_CS ->
K_RUN (incl. slicefield_wholeread_2lvl: its 738d7f4-era receive-spine
divergence no longer reproduces at the F1-merged base, verified
byte-id + 0/0). 989_lib_byteid checked: no DIVERGE entry graduates
(the test fails loudly on graduation; lib has no alias-base consumers
— the shape SEGVed before this fix).

NOT pinned (g-fold territory, #77/#78): direct alias-typed global
ARRAY rows. Expected state probe-verified UNCHANGED by this diff:
`let g: arr = [...]` -> ww link-ERR (no DATA emitted), cs 1-level
runs 0, cs 2-level runs WRONG (silent). The alias-GLOBAL base legs
added here (isglobalarr reclassify, global default-hi/cap) are
cs-aligned but runtime-unreachable until the DATA emit lands.

Residuals filed with the team: alias-blind elemsizeofc callers not in
the #60 pin family — cgun &a[i] addr-of (cgenexpr.ww:4638 region,
task #82), append() on an alias-typed slice local (:5287),
`alloc([], n)` into an alias-slice let (cgenstmt.ww:2159),
arr[i].field= float store (:8536); tagged-element READ under an
alias base keeps the ident-arm nullable semantics; checker
asserttyped on `untyped_lit * rangevar` over an alias slice
(pre-existing, check.ww is batch 4, task #80); uninit alias-to-STRUCT
zero-fill unchanged (correct: cstage fills composites);
range-destructure over alias-to-tuple-slice.

selfhost/cmd/{w6c,wwdump}/main.combined.ww regenerated (cgen*.ww are
embedded sources).
2026-06-05 20:14:49 +09:00
ac63951d54 w6c/cgen: #71 chained-dot walks chase alias-typed hops — direct offsets, byte-id graduation
rob probe-ruled F1 enrollment (fold-or-file decided by the dispatch
test): the chained-dot STORE walk, its READ twin, and the addr-of
sibling each single-peeled every hop's type, so an alias-typed field
(type fa = inner; outer{x: fa}) aborted the offset-folding fast arm and
fell to the generic address spine — store via cgplaceaddr
(PUSHQ/LEAQ/ADDQ/POPQ), read via ADDQ-per-hop. Runtime-correct BOTH
stages; byte-id NO vs wwstage's folded direct MOVQ offsets
(reviewer-62r diamond find). Chasing the walk hops (+ the read arm's
leaf gate and the two ptr-root sub peels) flips cs onto the fast arm =
wwstage's asm exactly.

Blast radius measured per rob's caution: bootstrap asm cmp-identical
vs the pristine 738d7f4 scratch on all five main.combined.ww, 989
lib_byteid pins unchanged (31/9/3), every control row byte-id — the
flip moves ONLY the #71 shapes.

test: 944_alias_accept_run +2 rows, both decl orders: nested store +
last-field readback; the fwd row adds the &v.y.b addr-of + deref-write
leg. Mutation-checked at the 738d7f4 scratch: both rows byte-id-diff
there; 70/70 green here.

Also graduates the reviewer-F1 slicefield rows: their `.len` readbacks
ride this walk, so the 7 K_RUN_CS rows from commit 1 flip to K_RUN
(byte-id) here.
2026-06-05 18:34:15 +09:00
c138605563 w6c/cgen: #62 Layer-2 (cs half) — widen store/push su chases the alias chain
The tagged widen's source classify (`su`) single-peeled: a 2-level
chain ali->base->struct left su TY_NAMED, so an alias-NAMED struct
union member fell past the struct arm to the SCALAR arm — word0-only
payload, words 1+ zero-filled. At normal decl order this was BOTH-
WRONG-IDENTICAL with wwstage (byte-id YES, gate-blind; F0 m5b_match1
exit 2/2). Two sites, the only widen entries: cg_widen_tagged_store
(let/assign/match BP path) and cg_widen_tagged_push (the call-arg twin
— surfaced by an F1 probe: fn((void|ali)) arg ran 1/1 both-wrong-
identical). The variant TAG still keys on the un-chased st — the
member's nominal identity is the alias (cg_tag_for_variant), only the
copy-width classify chases.

CS-ONLY half: wwstage's twin (rhsstructpayload name-keyed structlookup
+ its push twin, selfhost/cmd/wcc/cgenutil.ww:3062 vs structlookupchain
:1691) lands in F2 per the serial plan — until then these shapes are
transiently cs!=ww (was identical-wrong). Bootstrap asm cmp-identical
vs a pristine 738d7f4 scratch build on all five main.combined.ww, so
the 990-997 byte-id gates are untouched.

test: 944_alias_accept_run +6 rows from the banked set
/tmp/impl62r_layer2_rows.md — L2-1/2 norm+fwd store, L2-4 3-word width
loop (last payload word checked), the push-twin arg row, L2-5 base
control (ken's gold invariant), and L2-3 (`v as ali`) pinned at task
exit 2/2/2/1 there; 64/64 green here.
2026-06-05 18:33:47 +09:00
b9dd29706b w6c/cgen: #61 alias-NAMED struct param classify — transitive chase at the ABI choke
A 2-level alias param (`type row = st; type st = struct{a,b,c}`) fell
through the single NAMED peel at every classify site, so BOTH ends of
the call moved one eightbyte of a multi-word struct: the caller's
node_isstructarg/node_isaggarg said scalar, the callee prologue spilled
ONLY DI, and s.b/s.c read 8(BP)/(BP) — saved-BP/return-address garbage.
SILENT runtime-wrong (F0 m5_arg/m8_arg1/m8b_arg1lit: cs exit 1, ww
correct, byte-id NO).

Route the four classify chokes through type_chase_named: struct_arg_size
+ aggarg_size (shared by caller push AND the size axes), struct_float_
class (the #165 SSE eightbyte leg), and the fn-prologue param classify
pu. Caller and callee key off the same helpers, so the pair cannot
half-land. cs converges to wwstage's already-correct asm — all probe
rows graduate to byte-id YES; bootstrap asm cmp-identical vs master
(2-level alias params unused in selfhost).

test: 944_alias_accept_run +5 rows — fwd-ref / lit-init / 40B
5-eightbyte aggarg leg / f64 struct_float_class leg, every row checking
the LAST field with distinct values, + base-named control. Mutation-
checked at 738d7f4: the four alias rows exit 1 (the silent-wrong
signature) and byte-id-diff there; 55/55 green here.
2026-06-05 18:33:47 +09:00
9bd0d8bc81 wcc: #5 F1 promote type_chase_named + transitive-peel acceptance align-cs-up
Promote type_chase_named from cmd/w6c/cgen.c (static) to cmd/wcc/type.c
(exported via ww.h) and re-route every checker single-NAMED-peel through
it: check.c's ~28 inline ternaries + 3 ad-hoc loops, type.c's
assignability/untyped/borrow/opaque peels. type_eq's nominal identity
(check.c:114) and the resolve machinery guards stay untouched.

The re-route IS the acceptance align-up — cstage loud-rejected alias
shapes wwstage accepts AND runs Hare-right (F0 census, harec dealiases
at every consumer):
- #54 binop alias-vs-base: unify_arith gains the harec type_promote arm
  (ref/harec/src/check.c:1083-1105) — one-sided alias + dealias-equal
  promotes to the ALIAS side; alias-vs-alias stays rejected.
- alias-cond family: if/for/&&/||/! chase-then-bool (harec
  check.c:2141/2515/3229/3572). assert stays loud (F0 2a symmetric).
- #70 field access through 2-level alias chains (ken c3_chain3).
- assignability through the full chain (harec types.c:989-996
  dealias-both): return/init/assign legs, F0 8b idx/slice walls.
- alias-of-ptr deref (harec types.c:19-22 type_dereference).

The widening reaches cgen arms whose own single peels then misbehaved —
both classes are closed IN THIS COMMIT so no intermediate state ships a
loud->silent flip (bisect no-silent invariant):
- index family: the 8b acceptance hit ptr-load base + esz=1 (SEGV /
  prefix-luck) — idx_eff + the N_INDEX read / index-write / &base[i] /
  N_SLICE (expr + call-arg) / N_FORRANGE / aggarg_srcaddr-index /
  castsrcprim-dot / match-field base classifies chase.
- kind classifiers (ken #61-root-verify v3 find): a 2-level f64 alias
  param reached cg_isfloat's single peel and classified INT — silent
  wrong-register-class. cg_isfloat / type_isf32 / fld_isfloat /
  type_isstr / type_isslice chase. ken's v3 row is pinned with credit.

Bootstrap asm is byte-identical before/after (w6c on every
main.combined.ww cmp-equal vs a pristine 738d7f4 scratch; 989
lib_byteid pins unchanged): 2-level chains were checker-walled pre-F1,
so no previously-accepted program changes shape.

test: 944_alias_accept_run (20 rows): acceptance graduations pinned
runtime + byte-id both stages; idx/slice/range/slice-param rows cs-only
until the wwstage #60 esz family lands (F2 batch 1); cs-only
harec-parity loud pin for alias-vs-alias binop; assert stays-loud row;
ken-v3 + f64/str/slice kind rows. Mutation-checked at 738d7f4.

reviewer-F1 fold — the same invariant, outside the F0 census: this
commit ADMITS 2+-level alias slice/str/aggregate types in STRUCT FIELD
position, therefore this commit must keep them correct-or-loud. The
cgen FIELD-TYPE gates single-peeled, so the slice/str 3-word arms fell
to word0-only scalar tails — accept-and-corrupt, ww correct, every
shape loud at the pristine base. Chased (probe-proven, byte-id
graduations): single-dot field store + via-ptr twin, struct-lit fill,
chained store-walk LEAF (the #71 walk chases hops, not leaves),
chained-ptr-field store, single-dot / via-ptr / chained-walk field
reads (clobber-probed — word0 reads luck-passed on stale BX/CX). The
six unprobed sibling gates (indexed-elem store/read, ptr-chain read,
heap fill, tuple-elem read, static emit) hard-error via
fld_alias_tripwire on a 2+-level alias over an aggregate base, citing
task #73 (the family's scheduled chase); <=1-level and scalar bases
never fire — zero behavior change for any pre-#5-legal program (five
selfhost mains cmp-identical vs the pristine 738d7f4 scratch).
test: 944 +11 rows (9 K_RUN byte-id, wholeread K_RUN_CS [#60 ww half +
pre-existing 1-level read-spine divergence], #73 tripwire
K_BUILDERR_CS pin); 1-level controls per gate in /tmp/revF1.
2026-06-05 18:33:47 +09:00
738d7f481c wcc/check: #62 typedecl layout is decl-order-INDEPENDENT — demand-resolve forward refs + loud cycle guard (#69)
check_file resolved typedecl bodies in file order with an eager
under->size copy, so any body referencing a typedecl declared LATER
read its size-0 placeholder and baked it in: alias size 0, tagged-
union maxsz 0 (the F0 m5_match $48-frame under-allocated box), struct
field offsets collapsed, array element stride 0 — a whole cstage-only
family (7 size()-probe rows, all cs-fail/ww-pass pre-fix). wwstage's
demand-driven tinfofornode was order-independent on every row, so this
aligns cstage UP to the measured runtime-correct side (the #263-era
ruling; rule 10's align-down governs acceptance surface, not layout
correctness). Oracle: ken /tmp/ken_62_oracle.md — union size is 8B tag
+ roundup8(max CHASED member size), a fixed point over the module,
never a function of decl order.

resolve_typename now resolves a referenced-but-unresolved typedecl on
demand via resolve_typedecl (cycle-guarded by Type.resolving); the
pass-1.5 loop funnels through the same helper. No consumer can see an
unresolved placeholder by construction.

CYCLE GUARD — #69 ABSORBED into this rider (rob's rider condition):
true typedecl cycles now LOUD-reject on BOTH stages — "circular type
dependency" — mirroring harec's in_progress check (ref/harec/src/
check.c:4767 "Circular dependency for '%s'"). Pre-guard: cs silently
sized cycles 0; wwstage HUNG on an alias cycle (`type a = b; type
b = a` — ken's hang probe /tmp/ken62/c1_cycle.ww, killed at the 20s
timeout) and stack-overflowed on a struct value cycle. The check sits
at the VALUE-position size consumers only (alias root, struct field,
array elem, tuple member, union member), so the legal pointer
self-ref (`type node = struct { next: *node }`, the io.stream shape)
stays accepted, byte-id. wwstage gets the twin tinfo.resolving flag
(lib/ww/typ.ww) + circularnamed in check.ww; its arm loud-STOPS
(os.exit) rather than accumulating — wwstage's AST-level alias
walkers (resolvealias, aliaslookup chains) follow TNAME->TNAME by
name, blind to the tinfo table, and spin on a cyclic alias graph even
after the table edge is cut to tyerr (measured); cstage accumulates,
its single-peel ternaries cannot loop.

TWO-LAYER SPLIT — this is ONE bug number (#62) deliberately split
across THREE commits (this rider + F1 + F2), per ken's sizes-correct ≠
payload-correct proof: in NORMAL decl order both stages size the box
correctly (16/24, frames $64) yet both still run exit 2 — the box
STORE is word0-only, a chase-blind copy-WIDTH lookup in cgen, NOT the
type table. EXPECTED-FAIL after this commit: m5b_match1/m5_match stay
exit-2 both stages (now byte-id BOTH orders; pre-fix the fwd order was
$48-frame divergent). The Layer-2 sites and destinations:
  - F1 (cstage): cg_widen_tagged_store single NAMED peel,
    cmd/w6c/cgen.c ~2464 — the type_chase_named census family.
  - F2 (wwstage): rhsstructpayload bare name-keyed structlookup, no
    alias chase, selfhost/cmd/wcc/cgenutil.ww:3062 (structlookupchain
    :1691 already exists).
Banked runtime payload-readback rows for F1/F2: /tmp/impl62r_layer2_rows.md.

Test 944_alias_decl_order_size_run: every size class pinned in BOTH
decl orders (sizes, named union, struct field offsets, array elem,
2-level chain — norm + fwd twins, prefix-luck-breaking last-word
readbacks), 3 cycle BUILDERR rows + the legal ptr-self-ref row,
(void|base) no-regress control; dual-stage + per-row byte-id (arrelem
rows byte-id exempt: pre-existing #60 index-over-alias divergence,
order-independent, cited at the rows). lib/ww/typ.ww is an embedded
source: both main.combined.ww regen'd + committed (freshness gate).
2026-06-05 10:46:17 +09:00
d14a23b85e cgen: #57 in-cap tuple cursor fill keys on the DECLARED element type — tagged elems from concrete rvalues widen, both stages
The N_TUPLE literal's stamped type is CONSTRUCTED from its elements
(check.c N_TUPLE keeps untyped/concrete element types; assignability
is consumer-side), so the in-cap cursor fill — count
(tuple_lit_gpwords/tuplitgpwords) + push (tuple_lit_push_elem/
tuplitpushelem) — never saw the DECLARED tuple type. A declared-TAGGED
element whose expr is a concrete rvalue (`return (5: size, 9)` into
(un16, size)) counted ONE word and skipped the widen entirely: 2 words
sent against the receiver's declared 3-word walk, every later element
read garbage. Both stages, byte-identical, gate-blind (ken /tmp/ken57
p8/p9: t.1 read entry-junk). The let-literal twin
(`let t: (un16, size) = (5: size, 9)`) and the tagged-SECOND-elem
shift broke identically (probes q1/q2). The over-cap (sret) arm
already walks declared params (#240/#22b) — only the in-cap path was
declared-blind.

Fix threads the declared tuple type into the ONE shared helper pair
and its two loop sites:
  - tuple_lit_gpwords/tuplitpushelem take the declared elem type;
    declared-TAGGED + concrete rvalue widens into the shared tagged
    scratch (cg_tagscr_slot/tagscradd + cg_widen_tagged_store/
    cgwidentaggedstore, the cgreturn tagged-@retscr shape) and pushes
    the box words; declared-TAGGED gates the SSE row off (a (void|f64)
    box rides INTEGER eightbytes). Tagged->tagged subset (eslot
    mismatch) louds — the #23/#40 widening-remap family.
  - cg_tuple_lit_to_cursor/cgtuplelittocursor grow a decl param;
    cgreturn's in-cap N_TUPLE loops thread cg_ret_type/c.fnret.list
    (the same pp/pt walk its over-cap arm does); the N_LET in-cap
    tuple arm passes the declared type for an N_TUPLE rhs; the bare
    cgexpr route passes NULL/nil (emission unchanged).

Ident-elem sources keep the existing slot-load push byte-identically
(t57_ident_no_regress); the CALL-elem tripwire stays loud (#41,
t57_loud_call_elem). RESIDUAL FILED, not folded (rule 11): the
N_MASSIGN destructure-reassign literal rhs routes through the bare
cgexpr path (decl=NULL) and stays silent-wrong — probe q5_massign,
task #64, cited at the massign arm both stages. The annotated
multi-let spelling (`let (a, b): (un, size) = lit`) does not parse
(both stages), so N_MLET has no declared-literal route.

941 rows t57_*: return (named + inline union), let-literal, tagged
second elem, float payload, bare-untyped payload (rides the #33
chooser through the new wire), ident anchor, loud CALL tripwire;
ken's adversarial shapes (tagged-MID elem, two tagged rvalue elems
incl. void, plain-f64 SSE coexisting with a declared-tagged box), the
in-cap/over-cap boundary loud (k57d), and the NEW #57 tag-remap loud
pinned. Pre-fix at e8977a4: p8/p9 rows exit 1, q1_let exit 1,
q2_mixed exit 2.

Task #57.
2026-06-05 09:42:50 +09:00
80e7096f25 cgen: #50 append/insert tagged-dst VALUE boxes PRE-grow, both stages
The #34 value-store dispatch's tagged arm ran the widen choke-point
(cg_widen_tagged_store / cgwidentaggedstore — which cgexprs the value
internally) AFTER cg_append_grow, so a value expression reading the
destination (xs.len) saw post-grow state. Hare evaluates arguments
before the call's effects; the scalar/str/slice arms already ordered
value-first. insert() inherited the bug through its desugar-to-append
(9861f73) — one boxing choke-point, so this is the whole fix. Box the
value into a fresh per-site @apptagscr frame scratch pre-grow (zero,
BP-rooted widen store), then raw-copy the finished box into the new
slot post-grow.

Consumer evidence: regex fold-5b's {,0} rows (reviewer-5b's mutant;
ken's corrected-root matrix /tmp/ken_silent, f50v4_tagged exit 15 both
stages, byte-id, gate-blind). lib/regex.ww:643-647's pre-bound
split_target workaround comment GRADUATES with this commit; the
comment update itself rides the next regex touch per the standing
rule.

test/807: 57 -> 84 fixtures. New rows: tagged_pregrow_val (f50v4
exact), scalar_pregrow_val (no-regress control), tagged_selfref_val,
tagged_str_payload, tagged_regex_minrep (the {,0} shape standalone),
tagged_append_pregrow_val (direct append, the fix site),
tagged_realloc_selfref_loop (ken k50a: old-base read across actual
rt_ensure base moves), tagged_seq_positions (ken k50b: sequenced
inserts at 0/mid, each len-reading) and tagged_void_variant (ken k50c:
tag-only box through the fresh scratch). Mutation at e8977a4 fails
exactly the five eval-order-discriminating tagged rows, both stages.

The STRUCT-LITERAL value arm keeps its post-grow field-expr fill —
same eval-order class, different arm; filed (ww-core #59), cited
at-site, not folded.
2026-06-05 09:31:23 +09:00
24e02b259c wcc/check: inferred-let tuple literal carries its slot-layout size — 0-size local smashed saved BP/RIP (#44)
The N_TUPLE expr arm built its TY_TUPLE with size 0 (only the
annotated N_TTUPLE resolve_type route computed the layout), and
type_default passes TY_TUPLE through, so an inferred
`let t = (4: size, 2: size)` planted a 0-size local at offset 0 —
the element stores landed on the saved BP/RIP and main segfaulted
on RET (cstage; the arg shape instead fell to the global-symbol
path and link-failed). wwstage (exprtype N_TUPLE -> tinfofornode)
was runtime-correct throughout — cstage aligns UP to it; all
fixed shapes are now byte-id. Slot rule mirrors the N_TTUPLE twin
and cgen tuple_eslot, with untyped elements sized at their
type_default (element types stay untyped for the consumer-side
assignability contract).

7 table rows in 941 pin the class (cast/bare/mixed/float elems,
destructure-from-local, call-arg, nested); each fails at master
e8977a4 cstage (segfault or link-fail + byte-id NO).
2026-06-05 09:08:01 +09:00
e8977a413d cgen: #38a chained-dot TAGGED leaf — cursor read + widen store, value-chain + ptr-chain, both stages
A tagged leaf at the end of a dot chain (depth >= 2) fell to the
one-word scalar tail in FOUR walkers — the value-chain read spine,
the value-chain assign walker, the chained-*struct read branch, and
the chained-*struct assign branch — in BOTH stages, byte-identical,
gate-blind:

  READ:   one MOVQ pulled the TAG word only; is-tests passed by
          tag-luck while as/match/let consumers read stale DX/CX/R8
          as payload (ken x5c: o.r.min as size added entry-zero DX —
          want 28 got 20; ken b8 same through a mid-chain *rep).
  ASSIGN: one sized store landed the rhs ON the tag slot, payload
          kept its old bytes (ken x5d: `o.r.min = 8: size` made the
          following is-test false).

The single-dot arms were already correct — each fix is that arm
verbatim at the chained site:

  read:   the tagged cursor (AX=tag, DX=val0, R8=val2 before
          CX=val1 — the base may live in CX/AX; >32B box leaves its
          ADDRESS in AX, the #37 mem-read convention). wwstage rides
          the existing cgloadtaggedfield helper; the *struct-base
          branches stage AX into BX first (cursor targets AX).
  assign: full slot rewrite via the shared widener
          (cg_widen_tagged_store / cgwidentaggedstore, base BX/BP),
          plain `=` only — compound on tagged stays loud (#133).

The nested-LITERAL half of the old #38 filing dissolved: the
canonical fill's tagged-field widen delegation (#23, 413aafa) already
covers any depth — x5c's literal init emits a correct tag at HEAD;
only the chained READ/ASSIGN halves were live (probe-proven, asm at
/tmp/pB).

Probes converge byte-id and runtime-correct: x5c/d/e (28), depth-2
(b3/b9), (void|str) 32B box + match readback (b5), *outer root (b7),
mid-chain *rep (b8), non-tagged control (f38a). 938 rows chained_*
pin all six shapes. Residuals filed, not folded (rule 11): the
INDEX-spine sibling xs[i].min payload read (task #58); tagged-field
GLOBALS stay loud at DATA emit (#129 A.2), so the walkers'
global arms are mirror-only today.

Task #38.
2026-06-05 08:39:40 +09:00
9fad59354f cgen: #33 wwstage untyped-source variant match — mirror cg_variant_match's type_assignable arm
A bare untyped init into a tagged union resolved its variant through
taggedvariantindext's str/slice SHAPE fallback, whose first
non-str/slice variant can be void: `let e: (void | size) = 5` stored
tag 0 while the is/as side resolved size to 1 — runtime-FALSE is-test
on wwstage only (cstage resolves untyped sources in cg_variant_match
:801 via type_assignable; cs=0/ww=1 on ken's f33, divergent asm,
gate-blind).

Fix adds the untyped-source arm at the top of cgvariantmatch — the
single flatvariantidxt pass-1 predicate, same funnel position as
cstage — backed by tyassignableuntyped, a focused tinfo-keyed mirror
of type_assignable's untyped→typed subset (cmd/wcc/type.c:355-370)
plus its concrete→tagged variant drill (:316-324). Typed/loose
sources keep the shape fallback unchanged; tuple-in-union keeps its
AST-shape loud-stops (#241/#242, checked: TY_TUPLE is never untyped).

Probes converge byte-id: f33 let-init, assign-after-void, bool-leading
skip, bare arg widen, untyped-str, cast no-drift control. 938 row
untyped_int_bare_widen pins all of them (pre-fix ww_run=1 at 322667b).

Frees the fold-5b at-site #33 workaround (lib/regex/regex.ww:772
cast-form pin `min = (0: size)`) — the bare Hare-verbatim `min = 0`
spelling now tags correctly; the regex comment update rides the next
regex touch.

Task #33.
2026-06-05 08:38:20 +09:00
322667b820 cgen: #35/#46 Family C — tagged transport from deref/cast/unwrap sources goes mem-based, both stages
A tagged value reaching a transport consumer from a DEREF, CAST, or
UNWRAP source materialized as ONE scalar word — the N_UN(STAR) arm's
localloadop pulled word0 (the tag) and every cursor consumer then
transported garbage payload (cs pushed stale DX, ww stored 0/garbage;
divergent asm AND silent-wrong on both stages — ken f35 asm proof +
ken37v D3a/D3b). Consumers × sources × sizes all wrong: arg push,
let-init, assign, return, match scrutinee, as, widen — from *p at
16/24/32B, from identity/widening tagged casts, and from `?`/`!`
whose success variant is itself tagged (nested box: payload words
dropped). `is` and ww-match-16B passed only by stale-register luck.

Fix extends the #37 mem-based machinery (26d3754) below the 32B cap
instead of re-deriving:

  - cg_tagged_memread/taggedmemread: an N_UN(STAR) deref of a
    non-nullable tagged box is mem-based at ANY size — the pointer
    value IS the box address. The N_UN emitter skips the scalar load
    (joins the TY_FN/TY_ARRAY value-is-address skip); the existing
    size-generic memread arms in widen-store/match/as pick the source
    up unchanged, `is` loads the tag through the address.
  - arg push: the memread fatal becomes the mem-based push (words
    high→low from (AX)) — closes #35's word0-only push and wires the
    33-48B INDEX/DOT loud as a side effect. Pop side drains via
    pushargsrev's returned word count, unchanged.
  - cg_tagged_castpeel/taggedcastpeel: tagged→tagged casts are
    transport-transparent; peel at the call-arg widen decision and the
    widen-store entry so the ident/deref arms see the carrier and the
    remap keys on the operand type. The identity-only subset
    (cg_tagged_idcastpeel) applies at is/as/match, which key variant
    indices on the scrutinee's own type; surviving non-identity casts
    die LOUD there and at the widen cursor arms (rule 7) instead of
    word0 garbage. The peel also wires 929's >48B memarg same-type
    cast row (place resolves post-peel; loud pin flips to a run row).
  - tryprop/tryunw: IDENT sources load the cursor from their slot,
    memread sources from the box address (≤32B); >32B non-call and
    global tagged idents die loud (rule 7). A TAGGED success variant
    shifts the nested box past the outer tag (twin of the #241 tuple
    shift) — closes the unwrap-source half (ken unw16).
  - wwstage alignment UP to the cstage type-keyed routes:
    rhstaggedabicall admits N_UN deref + N_TRYPROP/N_TRYUNW (stamped
    type), matchscrutt carries the N_UN stamped type (spill size +
    variant indices; was nil → tag-0 clamp + mis-sized spill),
    cgreturn routes memread sources through the widener (the
    fall-through wrapped the un-deref'd POINTER as payload), and
    pushargsrev's aistagged gate admits the deref kind.

Emitters and consumers ship as ONE commit: they share the memread
contract, and splitting opens a transient window where a wired
emitter hands an address to an unwired consumer — the #61/#37
route-sharing fuse. The test flips ride along because they pin the
flip itself: 941's two #37 deref loud-symmetry pins become run rows
(the loud is now wired), 929's fail_rvalue_cast becomes
memarg_idcast_peeled.

No-drift bar held: ≤32B IDENT/INDEX/DOT sources emit byte-identical
asm vs master 4c46d3a (probe corpus nd1: ident let/match/arg,
struct-field, indexed element, call, nullable, ident-widen — both
stages IDENTICAL); is-on-deref is incidentally byte-identical too
(the tag load moved from the emitter to the consumer).

Tests: 941 grows 252→272 checks — ken's exact f35/D3a/D3b shapes,
each consumer × source × size cell (16/24/32/56B, str + struct
payloads), neighbor-guard row, identity-cast arg, widening-cast let
(payload checked — the old cs pass was is-only luck), success-first
unwrap-to-tagged + ident/deref unwrap, the 56B slice-deref let+match
flips (payload-pinned), and 2 rule-7 loud pins (global tagged `?`,
cast-to-third-union). Reviewer-C rows commit ken's remaining
adversarial shapes (gC1 deref-wrapped cast arg, gC2 void-variant
deref, gC3 slice-element-pointer deref, gC6 56B memarg-leg deref arg
— that one a regression pin, already place-resolved at base), a 40B
deref ARG (the 33-48B mem-push leg, silent word0 at base), and a
multi-arg pop-balance row (tagged-deref arg mid-list, called twice —
the original #35 1-push-2-pops symptom). At base 4c46d3a the impl
rows fail 53/254 (silent-wrong exits, cs≠ww asm, missing louds); the
reviewer rows kill at base too (flip rows LOUD, others wrong-exit)
except the gC6 pin. At HEAD 272/272 + 929 22 rows + test-unit 284.

Residuals (filed separately, pre-existing): #216 success-tag
divergence — error-FIRST unions emit CMPQ $1 (cs s_tag) vs CMPQ $0
(ww first-param) and ww's first-param success-type read misses the
tagged shift; match-expr tagged yield is cstage-checker-rejected
while ww runs it (the #34/#43 acceptance family); `as` binding a
STRUCT payload at let-init stays loud ("aggregate init from unhandled
rhs shape"); wwstage tagged-source arg-widen-into-WIDER-slot (the
pushargsrev #21-comment out-of-scope boundary, task #55) — the deref
leg of that family is now cstage-correct via the widen-store memread
arm but stays wwstage-silent-wrong (joins the pre-existing INDEX leg;
ident leg runs by prefix-luck under-push); truncating scalar cast as
a box payload drops the conversion (task #56, both stages, untouched
by the peel — scalar→scalar casts are never peeled).
2026-06-05 07:39:24 +09:00
4c46d3afde cgen: #49 aggregate-ASSIGN word0-only family — one mem-to-mem funnel (cg_aggcopy), both stages
Whole-aggregate reassignment `b = a` fell to the N_ASSIGN scalar tail
and copied ONE MOVQ — word 0 of any struct/array/tuple — in BOTH
stages, byte-identical, gate-blind (ken f49_min asm proof; latent
because lib style is let-init, whose #265/#268 copy is full-width).
Same class at three more positions: struct-lit FIELD init from an
ident source (`outer{.., r = r}`, the #38 non-tagged half), the deref
place `*p = s` (#31-A), and the module-let global `g = a` / `g = pt{..}`.

Fix: extract the C1.25 assign-resolver word-copy tail verbatim into
cg_aggcopy/aggcopy — the ONE place-resolved (SI)->(BX) aggregate copy
— and wire it at the N_ASSIGN ident-aggregate arm (local + global),
the deref-place divert into the existing resolver aggregate arm, and
the structlit-fill aggregate-field arm, all fed by aggarg_srcaddr
(the closed #265/#268 dispatch). The new arms key on the FULL alias
chase (type_chase_named / chased stamped tinfo, the #22 precedent) in
BOTH stages — the region's single-peel `lu`/`fu` would miss
`type b = a; type a = struct` on cstage while the wwstage twin fired
(ken R1, gA3b: master cs ran the word0 corruption, exit 2; now 0).
Non-addressable aggregate rhs (tuple-lit, unhandled call shapes) dies
LOUD (rule 7) instead of silently truncating: #31-E `*p = (3,4)` and
#31-G's deref flavor `*p = mk()` are now loud both stages (the INDEX
flavor `a[i] = mk()` stays in the legacy INDEX arm — receive
machinery, not this funnel; still filed under #31). #31-B rides: the
cstage-only <=24B gate before cg_structlit_fill_bp is lifted (the
wwstage twin never gated — a >24B literal reassign was
cs-zero/ww-filled, rule-10 break). Global structlit reassign rides
the existing DST_GLOBAL fill machinery.

Unsplit (rule 11): the assign arm, fill arm and deref divert all
route through the one new funnel (cg_aggcopy + aggarg_srcaddr) in
both stages; splitting by site or by stage would ship a transient
cs!=ww (gate-red) or a funnel with no consumer.

941 t2_reject_chain_arg: the row's tuple-LITERAL field fill now louds
at the #49 fill arm before reaching the pinned ARG-site reject; the
fill switches to an ident source (newly working via the fill arm) so
the original arg-site pin still fires.

test/wcc/812_agg_assign_width.c: 17 runtime-readback rows (the only
oracle for a gate-blind class) + per-row asm byte-id; every row fails
at 7545bf7 (ken matrix f49_min/f49b/f49c/fA_16b/f38b + gA3b/gA6 +
impl-A probes; reviewer-A re-probed 5 rows + the gA2 12B shape at a
master git-archive scratch). Alias rows use FIELD-WISE init: the
struct-LIT spelling louds earlier at the pre-existing task-#7
aggregate-let bound on wwstage (the #5 alias-arc's hole, not this
funnel's). Reviewer-A amendment (test-only, K5 self-certify): add
the ken-gA2 odd-size row (12B {u32,u32,u32}, maxalign 4 — pins the
MOVL tail; master both stages exit 3) and gA4's neighbor guards on
the deref row, completing ken's validated matrix in the committed
suite.
2026-06-05 06:18:45 +09:00
7545bf7dcd regex: fold 5b — repetition {m,n} (the { arm + run_thread inst_repeat + rep prefill)
All three loud bounds flip: the { metachar (ha:368-402, inclusive
advance bound), the run_thread inst_repeat arm (ha:669-684, is/as
verbatim per the #42 fence), and the search rep_counters prefill
(ha:763-765, count-loop respell of the sized-fill alloc). The
deferred-metachar table EMPTIES — every metacharacter compiles; the
POSIX class body is the only loud surface left in lib/regex.

Two silent compiler finds surfaced and filed, respells drew-signed:
#49 (whole-struct assign from a match binding w/ tagged fields
corrupts them context-dependently — the parse_repetition unwrap goes
field-wise in-arm) and #50 (insert() grows the dst before evaluating
its value arg, +1 split mis-target on Hare's len(insts)+2 payload —
pre-bound, the '?'/'|' arm convention).

Activation table: the {m,n} matrix (+test.ha:443-460) incl. the
open-ended (0,7) and {,0}de rows, the \{ \} escape pair, the :635
5a carve-out, the {0,}/{1,}/{0,1} twins (cross-spelling agreement
with their 5a */+/? siblings), the Various composed rows minus the
[[:class:]] row (POSIX abort, fold-4 ruling), findall fo{2,}, plus
ww-added multibyte {2} and long-input {1,} stress rows.
2026-06-05 04:50:27 +09:00
c0c15945be regex: fold 5b — parse_repetition leaf (ha:486-545)
Dead until the { arm lands (tranche-A precedent); 13-row direct
private-fn table, error texts byte-exact. Hare's verbatim
((void|size),(void|size),size) tuple return can't cross a union
boundary yet — cstage's (tuple|error) return store is cgen-unwired and
wwstage's variant-match rejects the tuple case arm (filed #47) — so it
respells as the private repparts struct per the scope-fold5 §3
pre-signed fallback; graduates back to the tuple when #47 closes.
Riders: call-result .N tuple read loud-rejects (filed #48, bind-first
local); strings.index's standing i32 convention (#8) stays internal,
widened at each size boundary; ha:494's same-name re-bind is rejected,
second local feb.
2026-06-05 04:48:56 +09:00
26d375410e w6c+w6c_ww: >32B tagged INDEX/DOT reads go mem-based — box address in AX (#37)
A tagged box wider than the AX/DX/CX/R8 cursor (size > TUPLE_GPCAP*8)
read via N_INDEX or N_DOT now leaves its ADDRESS in AX — joining the
sret-call mem-based class the #38b gates already speak — and every
cursor consumer branches on one shared predicate (cg_tagged_memread /
taggedmemread) before reading AX as the tag. <=32B keeps the cursor
byte-for-byte (32B-at-cap asm proven identical to base on both the
t.N and INDEX routes).

Emitters: N_INDEX ident+fallback arms, N_DOT tuple-element (flips the
#22b loud bound), N_DOT struct-field + ptr-chained-field (were silent
clamps at 32B); wwstage twins gate cgloadtaggedfield at the helper
choke-point. Consumers: match spill x2 and the widen-store subset +
nested arms (the let/assign/return-widen/arg-widen/vararg choke-point)
copy the box from memory, then share the existing zero-pad + tag-remap
tail; `is` loads the tag through the address; `as` spills mem-based.
Rule-7 loud bounds replace silent cursor garbage for the non-mem-based
>32B kinds, `?`/`!`, and the 33-48B in-reg tagged arg push (mem-based
push stays the #35 family); the exact-type >32B return passthrough
from INDEX/DOT flips from its #38b loud into the widener route. The
pre-existing >48B memarg stack blit (cgplaceaddr) never used the
cursor and is pinned unchanged.

Reviewer-37 amendment: the non-mem-based >32B loud was ONE-SIDED on
two wwstage routes — cgwidentaggedstorebp had no fall-through guard
at all (`let w = *p` on a 56B box: cstage loud, wwstage silent word0
truncation), and cgmatch's guard keyed on matchscrutt-resolved
spillsz, which defaults under cap for kinds matchscrutt can't resolve
(N_UN deref et al), so `match (*p)` slipped it the same way. Both now
loud off the stamped src/scrut type_ (the kind-blind key cstage
already uses), restoring the rule-10 symmetry the body claims.

Emitters and consumers ship as ONE commit: they share the memread
contract, and splitting would open a transient window where a wired
emitter hands an address to an unwired consumer (silent garbage) —
the #61-precedent route-sharing fuse. The CX-global-tuple-base LEAQ
arm is TRIPWIRE wiring: a >32B tagged global-tuple element is
unreachable today (module-level tuple inits are int/str-literal-only;
tagged elements loud at the DATA emit), and the LEAQ keeps the same
base_reg generality as the cursor walk it replaces (ken note, task
record).

This was the last 5b compiler gate: `match insts[pc]` on the regex
inst union (inst_lit|inst_repeat, 56B) was silent-wrong gate-blind
byte-id on both stages (payload words 3+ dropped past the R8 clamp).
test 941 grows 165->200 checks: the #22b BUILDERR pin flips to a
runtime row, plus the 56B driver match, str+nested-tagged payload,
let/is/assign, indexed return, widening (identity and reversed-order
remap), 56B memarg, ken's X1 composition row, 32B-at-cap INDEX
boundary, the ptr-chained p.f match (BX-arm) and (*p)[i] fallback-arm
rows, and the two deref loud-symmetry BUILDERR pins. At base f272068
the 11 bug rows fail (2 BUILDERR flips + 7 silent-wrong + 2 missing-
loud pins, both drivers); the anchors pass. Oracle: ken PASS at
bf21964b pre-amendment; re-bind on the amended tree pending (source
bytes changed: cgenutil.ww/cgenexpr.ww louds + combined.ww regen).
2026-06-05 04:06:19 +09:00
f272068940 test/run: missing test binary is a loud FAIL, not a silent SKIP
A test/wcc/NNN_*.c with no Makefile-wired binary used to write a SKIP
status, count toward "all N tests passed", and leave MAKE_TEST_EXIT=0.
Two live instances: 953_arrlit_slice_run skipped under a green gate for
weeks (committed unwired at bf1037d, wired in the previous commit), and
a 938 hit the same hole mid-gate. The runner now treats a missing binary
as FAIL naming the file and the target to add, touches the .fail marker,
and so flips the summary + exit code. Closed by construction: make test
builds every $(TESTS) target before the runner walks test/wcc/*.c, so
the missing-binary path is reachable only by an unwired file — there is
no legitimate missing-binary SKIP to preserve. Intentional skips keep
their existing visible forms (UNIT-mode non-enumeration; in-test per-row
skip messages), neither of which folds into the pass count.

Count math: the summary N is unchanged (a SKIP already incremented ran);
what changes is honesty — pre-fix "all N passed" could include silent
skips, post-fix every counted test actually executed. A bare sh test/run
without the make-built binaries now fails loud instead of green-skipping
the whole suite.
2026-06-05 03:01:32 +09:00
ec7e8af6e9 Makefile: wire 953_arrlit_slice_run (committed unwired at bf1037d)
The test .c landed with the #25/#31 fix but its $(BIN)/test_arrlit_slice_run
target was never added, so the runner SKIPped it on every `make test` since
— while it still counted toward "all N tests passed". Wiring per the
953_arraytoslice_run pattern; the test passes 13/13 at HEAD (cstage run +
cs==ww byte-id + reject rows). The runner-side hole that let an unwired
test skip silently into the pass count is closed in the follow-up commit.
2026-06-05 03:00:08 +09:00
a72e8158c7 w6c+w6c_ww: over-cap (sret) tuple returns carry tagged elements — MEMORY-class box copy (#22b)
The over-cap SEND gains a per-element tagged arm: a declared-TAGGED
element copies its whole box mem-to-mem through the sret pointer
(@sretarg reloaded into DX, eslot/8 AX-bounced word pairs) from its
ident-local slot — cgexpr can't source it (tagged ident load is
word0-only; the AX/DX/CX/R8 box cursor would collide with the DX
dest-base reload). Non-ident/widening sources stay loud (call-source
is task #41 per rob's loud+filed ruling, widening #23/#40). The
receive legs were already eslot-generic from #22a except the wwstage
MASSIGN sret copy-out, whose wide=(str||slice) key dropped a tagged
element to the scalar 8B branch — re-keyed to the accessor's eslot>8.

The over-cap DECISION now rides cg_sret_retsize/sretretsize on the
DECLARED return type — the same SSoT the prologue @sretarg wiring and
the caller use. The old expr-shape count disagreed with the classifier
for declared-tagged elements with unwidened-payload exprs: the emit
took the register path against an sret-reading caller — built clean,
ran garbage, BOTH stages, byte-id, gate-blind (941 b22_reject_widen_
skew). A rule-7 net behind the branch catches any future inverse skew.
The same classify/emit class on the LET side: wwstage's cgexpr cursor
materialiser counts a void literal elem as 0 words, so a void-bearing
over-cap literal init slipped its loud and fell past every store arm
to NOTHING (cs louded via the ken-R1 net; ww ran silent-wrong) —
wwstage gains the same net. foff advances ride tuple_eslot/tupeslotn
(THE accessor): ken's K4 proved the old wide?esz:8 void-elem 8-advance
was a LIVE silent miscompile at base (declared-void-elem over-cap
return, send wrote +8 where every receive walks the 0-slot).

The unwiring also made >32B boxes transportable, exposing the t.N
tagged read's unbounded k<eslot/8 cursor walk: cstage OOB-indexed
tuple_rseq[4] (invalid asm), wwstage tupreg-clamped k>=3 to R8 (silent
payload-word-3 drop). Bounded loud both stages; the mem-based box read
is #37's fold (transport itself is size-generic and correct — 941
b22_big_box_mlet). One commit by the #61 route-sharing precedent:
unwiring the send is the only thing that makes the ww massign arm,
both skew nets, and the >32B read reachable — the one split order
that avoids a transient silent-wrong build (send last) lands those
arms DEAD, unexercisable by any test until the unwiring commit, so a
defect in them would bisect to the wrong commit; fused, every arm
lands with the 941 rows that pin it, preserving the #22a exit
invariant (zero silent tagged-tuple paths: correct-via-accessor or
LOUD with a task cite; proof-greps in-tree — tuple_ebytes/tupebytes 0
hits, all remaining tuple_rseq/tupreg walks cap-validated upstream).

Driver: regex fold-5b parse_repetition ((void|size),(void|size),size)
= 5 GP eightbytes > TUPLE_GPCAP — pinned end-to-end (construct, send,
let/MLET/MASSIGN receive, t.N is/as, destructure) plus head/mid/tail,
str+tagged and SSE-over-cap mixes, `_` skip, and the reject pins: 941
165/165 (15 new rows), test-unit 281/281. ken runtime-oracle PASS x2
(independent hand-math layout audit, K1-K5 probes); reviewer-22b
amendments (test-only): ken-K5 32B-exact box-at-cap row, both-tagged
2-elem row, chained-sret row, #37-family cite pinned in the big-box
experr. Residuals filed: #39 return-forward divergence, #41
call-source tripwire, #37 mem-based box read.
2026-06-05 02:47:14 +09:00