24ca570a7eb590789f223003ea07de92b07bd17c
1037 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 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
|
|||
| 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. |
|||
| 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, |
|||
| 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 |
|||
| 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 (
|
|||
| 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). |
|||
| 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). |
|||
| 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). |
|||
| 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
|
|||
| 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
|
|||
| 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
|
|||
| 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 |
|||
| 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). |
|||
| 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
|
|||
| 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 ( |
|||
| 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
|
|||
| 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,
|
|||
| 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
|
|||
| 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 ( |
|||
| 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
|
|||
| 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.
|
|||
| 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.
|
|||
| 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
|
|||
| 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
|
|||
| 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. |
|||
| 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. |
|||
| f88dbb01e2 |
wcc_ww/check: inferred struct-lit let plants the synthesized TNAME — field(SB) name-leak + tagged-field assign bound (#24)
For an annotation-less `let p = pt{...}` checkletassign planted exprtype's
N_STRUCTLIT result — the struct decl's BODY node (N_TSTRUCT, per #66) — as
the let's type. Every cgen local-arm dispatch (cgdot read, cgassign
tagged-field store, the alias peel) is N_TNAME-keyed, so the body matched
no arm: field reads fell to the module-qualified fallback and emitted the
FIELD NAME as a global symbol (MOVQ f(SB) — link-fail, #211 name-leak
family; silent corruption if a same-named global exists), and a tagged-
field assign fell to the assign-resolver TY_TAGGED loud bound. Both PG5
wwstage symptoms, one root; plain structs leaked too. Normalizing the
inferred binding to the synthesized TNAME (mktname + tinfofornode stamp)
routes every consumer down the already-byte-id annotated path. cstage
needs no twin: check.c:1477 clet carries Sym.type (tinfo) and its
emission is annotation-invariant (probed). Test 811: 10 rows x 2 drivers
+ 10 asm-byte-id; pre-fix wwstage link-fails every unannotated row
(incl. the `...` autofill and parenthesized forms; nested s.f.g ran
but cs!=ww asm).
|
|||
| 413aafa599 |
w6c+w6c_ww: tagged-union struct-lit payload fills via the canonical fill (#23)
The widen choke-point's struct-payload arm carried its own inline N_STRUCTLIT field loop -- a parallel fill that drifted from cg_structlit_fill/cgstructlitfill: no tagged-field widen arm, so a (void|T)-typed field's raw scalar landed in the field's TAG word (silent truncation past the first tagged field, both stages, byte-id, gate-blind; prober-9 PG5). Delete both loops and delegate to the canonical fill at the payload base: one fill path, one widen path, mutually recursive. Inherits the nested-struct/call/arrlit field arms and closes a latent fsz==2 cs!=ww (old ww loop's fieldstoreop MOVW vs cstage MOVQ). Test 938: 15-row table-driven runtime readback (incl. ellipsis autofill, offset-0 tagged field, (void|str) payload, 3-level widen-fill recursion torture), all 13 bug rows silent-fail at master 6699158; 2 rows skip the byte-id check loudly (pre-existing match-on-tagged-FIELD readback cs!=ww, master-confirmed, separate family). |
|||
| 66991585d6 |
wcc+w6c+w6c_ww: tagged tuple elements take their full slot — tuple_eslot accessor (#22a)
slot = roundup8(size(elem)) — 8B is a FLOOR, not a ceiling (user- ratified 2026-06-04; the #237 fieldslotsize precedent; (str,str)=48B predates this — tagged was the one truncated >8B kind). Pre-fix the checkers truncated a tagged element to one 8B slot and every cgen transport walk strode wide=(STR||SLICE)-else-8: cstage read the NEIGHBOR slot, wwstage read ZEROS — both-wrong-differently, so the byte-id gates were blind (prober-9 PG1, /tmp/p9). One stride accessor per stage — tuple_eslot (cgen.c) / tupeslot (cgenstmt.ww) — now feeds every tuple walk; the per-site predicates are deleted as absorbed. Sizer: check.c N_TTUPLE + check.ww tupleelemslot gain the TY_TAGGED arm (astsize already rides the type table since commit 0). Routes flipped to the accessor, both stages symmetric: cursor producers (lit/slot-to-cursor; tagged ident elements load their box from the slot — cgexpr's tagged ident load is word0-only), let-receive (tuple_store/tupstore generalized to eslot/8 words), N_RETURN send, by-value param receive, arg restage/drain (tagged stays loud per C-t2), destructure (MLET/MASSIGN, ident + sret + in-cap), t.N read + len(t.N) + global-g.N offset walks (t.N gains the tagged box load: AX=tag, DX/CX/R8=payload — the is/as spill cursor), sret classifier, DATA emit. wwstage cgtagvariantidx gains the #67 stamped-carrier arm (flatvariantidxt on .type_) — its AST-only key silently clamped 't.0 is size' to tag 0; fused here because the tuple-element read this commit wires is its only exercisable consumer. Exit invariant: zero silent tagged-tuple paths — in-cap shapes (<=4 GP eightbytes) are correct end-to-end; everything else is LOUD: over-cap sret return (#22b, task #28), call-arg (C-t2 #32), non-local literal element sources (#22b/#23), tuple-in-union payload (#242/#22b), global static-init, element write (pre-existing). Closure proof-grep at HEAD: 'tuple_ebytes|tupebytes' -> 0 hits; 'wide.*=.*(TY_SLICE|TY_STR)' tuple-walk survivors are all behind tagged loud-guards (cgen.c:2535/2568/12013 widen-store + over-cap send; cgenutil.ww:3527 twin). Latent cross-checks closed by the accessor: wwstage MLET-sret strode esz (4 for i32) vs cstage 8; wwstage param-receive strode slotsize (composite slotsize) vs cstage 8; both now the accessor's floor-8. Tagged inits in pins use the CAST form (5: size) — the bare untyped-int widen-store mis-tag is pre-existing at master and filed (task #33). 941 gains 13 rows: t22_* size/align folds (+ void-elem 0-slot pin), the full runtime round-trip (read, is/as, both element orders, void variant, destructure, literal-let), two-tagged-elem in-cap and float+tagged in BOTH orders (ken k1/k2 regression-pinned as rows), t.N-as-call-arg, 5 loud pins (arg, over-cap return, call-source element, global init, element write), and the sret_narrow_mix_* triple below. Runtime rows exit-checked under both drivers + byte-id. 129/129; unit tests green; sizelint clean. ken R1 (re-validation fix): an OVER-CAP tuple init whose rhs is not a CALL fell past every cstage N_LET store arm to NOTHING — silent uninitialized-frame reads — while wwstage loud-rejected the same shape. Pre-existing for (str,str) literals; the #22a tagged slots routed >16B-box tagged shapes into it (k5b/k5c/k5d, base-correct because base truncated them in-cap). cstage now routes the rhs through cgexpr (the cursor materialisers carry the exact wwstage loud texts) with a trailing fatal as the net; 941 pins both the tagged and the (str,str) spellings of the hole. reviewer-22 (review fixes, folded): the MLET-receive slot flip above landed ALONE on the wwstage over-cap sret family — the ww RETURN-send and MASSIGN-receive still strode packed esz (4 for u32), so `let (a,b,c) = f()` over (str,u32,str) read c at slot offset 32 while ww's send wrote it packed at 28: a ww runtime REGRESSION vs base (base was packed-consistent on both sides — ran right, byte-diff). Both walks now stride the slot (send mirrors cstage's `wide ? esz : 8`; MASSIGN strides tupeslotn) — closing, in the same stroke, the pre-existing base skews: ww `let t = f()` over-cap narrow-mix read (reader slot-laid vs send packed, runtime-wrong at base) and the cs≠ww asm on all three routings. Second find: t.N tagged element as a CALL ARG — cstage rides its generic node_istaggedarg cursor push, but wwstage's kind-gated aistagged missed N_DOT and mis-routed the box into the widening branch (taggedvariantindex -1 clamped to 0: callee read variant 0, silent, ww-only). cgenutil.ww gains the N_DOT arm (aistagged + pushargsrev), the #67 stamped-carrier twin of the N_INDEX (#12) arm. The N_DOT arm was a LIVE wwstage stdlib miscompile: hextest's import graph carried two t.N tagged-arg sites (base .s diff = exactly the clamped-tag PUSHQ pair) — the 989 lib-byteid ratchet caught the convergence and #59.4 graduates to M_ID (runtime-correct per 979_hex_run). Probes: /tmp/r22. Residual filed: tagged arg from deref/cast/unwrap sources is a word0-only read on BOTH stages (pre-existing, base-confirmed) — generalizing wwstage's kind gate to cstage's type-keyed check rides that task. |
|||
| ed62e8199f |
wcc_ww/check: tuple size() fold reads the type table — packed-sum C-t0 escape (#22 commit 0)
The wwstage size()/align() fold walks the AST (astsize), and its
N_TTUPLE arm still summed PACKED element sizes — C-t0 flipped the
checker type table (tupleelemslot) and cstage's N_TTUPLE to the
ratified slot layout but missed this second wwstage sizer.
size((u32,u32)) folded to 16 on cstage and 8 on wwstage: a silent
cs≠ww in every folded tuple-size constant, plus the recursive
escapes (a tuple inside struct/array size computation under the
fold). Runtime-confirmed at
|
|||
| a2c2bbc6b1 |
regex: fold 5a — capture groups (compile ( ) arms + run_thread groupstart/groupend + search spread)
compile: '(' (ha:317-323) appends inst_groupstart(capture_idx) and
grows jump_idxs per level; ')' (ha:324-334) appends the void
inst_groupend, fixes up the level's pending alternation jumps (#70
range + #58 assert), range-deletes them (#8) and closes the level;
the loop-exit done arm gains the Unmatched-'(' check (ha:277-282).
The anchors' group_level arms, the postfix inst_groupend/groupstart
arms and find_last_groupstart's success path go live unchanged.
run_thread: inst_groupstart (ha:636-652) fill-grows captures to
idx+1 (count-loop spelling of Hare's 3-arg fill-append) and opens
the group with the SIZE_MAX end sentinel via the #20-fixed indexed
struct store; inst_groupend (ha:653-668) closes the innermost
unclosed capture (ha:655's 2-clause for respelled — ww has no
cond;post form) and slices content from the bytesize span through
the addr-of-element pointer.
search: the ha:820 loud bound flips to the real capture spread
(#35/#25); the pad fill self-activates for unset trailing groups.
Tests: ( ) graduate from the metachar-loud table into real-text
error rows (+ ww-added anchor-in-group / Unused-on-groupstart
re-verify rows); hand-built groupstart/groupend arm cases; the 5a
find table (+test.ha:257-275 group/alternation, :499-503/:607 jump
bugs, :610-621 submatch family, :635/:640 alternation-group,
:649-665 nested minus the 5b {m,n} twins, + ww-added multibyte
row) with len(res) pinned per row; submatch content rows
(+test.ha:704-708 + ww-added multibyte) — the 5a acceptance gate.
|
|||
| 0e61db1857 |
regex: fold 5a — add_thread dups parent captures/rep_counters (ha:568-573)
The loud bound (capture dup not yet portable, #35/#34/#7) flips to the real dup now that #35's spread place-chain sources landed: fresh slice header + spread-append, the D3 spelling of Hare's alloc-dup. The ok/defer-if frees drop (free() is the documented no-op, #27). Dup-independence rows drive add_thread directly: values carried, backing independent both directions, empty parent → empty dup. |
|||
| 06b0fea98b |
w6c+w6c_ww: struct-lit store into indexed/deref/field place fills via resolver (#20)
A struct-LITERAL rhs aimed at an N_INDEX element (a[i] = pt{...},
(*ts)[i].caps[k] = capture{...}), an N_UN deref place (*p = pt{...}),
or an indexed-base FIELD place (a[i].f = pt{...}, reviewer-20 sibling)
fell to a scalar store tail in BOTH stages: cgexpr on a struct
literal emits nothing (AX=0) and one MOVQ zeroed the place's first
word — every field silently dropped, a str-leading element's
content.ptr nulled (downstream SEGFAULT). Byte-identically wrong, so
every byte-id gate was blind; runtime pins added.
Fix: divert struct-lit-rhs INDEX/UN-STAR/DOT-over-INDEX places past
the legacy arms and widen the F6 assign-resolver gate
(N_DOT -> N_DOT|N_INDEX|N_UN); the existing C1.25 aggregate arm
materialises the literal into a fresh per-use @placescr slot and
word-copies to the cgplaceaddr-resolved address. No new path;
@placescr alloc site stays single per stage. Rider (task #32): an
array-LITERAL rhs at assignment — unwired for EVERY place kind, same
silent zero-word tail — now dies loud at one choke-point until the
fill lands; build-fail rows pin it.
Gates regex fold-5a (run_thread groupstart capture store,
regex.ha:643-651). Residual adjacent gaps (deref ident-rhs truncation,
>24B ident reassign cs!=ww, struct compound acceptance, value-global
DATAW, tuple-lit deref truncation, CALL-rhs RAX-only store) probed
pre-existing and filed as tasks #31 A-G / #32.
|
|||
| 0ce98cc5dc |
wcc+w6c_ww: append() spread place-chain sources via cgplaceaddr (#35)
#35 (fold-5 blocker, PG6): the append() spread arm dispatched on SOURCE node kind — ident-local only; both fold-5 dup shapes loud-rejected on BOTH stages: deref-spine `append(dup, (*tsp)[0].caps...)` (add_thread regex.ha:569/572) and indexed `append(res, ts[0].caps...)` (search ha:820). Route every non-ident-local source through cgplaceaddr (the established place choke-point: C1 stores, C1.25 aggregate fields, FA1 append targets, #49 element sources): the source header ADDRESS resolves ONCE, pre-grow, into a fresh per-site @appsprscr spill, and every loop iteration re-reads .ptr/.len THROUGH the spilled header after the grow. This adapts the #49 split ruling to the spread's full-range copy: the chain's rvalues (deref-root pointer expr, index exprs) evaluate exactly once PRE-grow — an index reading the dst's len sees the pre-append value — while the source data base and len re-derive live each iteration, so a source header aliasing the dst header re-roots post-realloc. A header reached through a buffer the grow reallocs reads the STALE copy — bit-identical under the non-reclaiming rt/malloc, the same documented #49 stale-base hole (pinned by the spread_selfalias_chain row). Ident-local sources keep the legacy BP-disp emission byte-identical. New loudness with the same fix: the spread source's stamped type must chase to TY_SLICE/TY_STR — a [N]T array ident source previously read its first 16 DATA bytes as a {ptr,len} header, silently. Array wiring plus the remaining rvalue sources (CALL, slicing exprs) stay loud, filed task #27. Global-ident sources now resolve on cstage but are blocked by a pre-existing wwstage checker reject ("let: not assignable", task #29) — no dual-stage row until that closes. 806_append_place: reject_spread_src GRADUATES to a runtime row (spread_place_deref, the old reject source verbatim + readback); new rows spread_dup_copy (PG6 verbatim: both fold-5 shapes, 56B capture elements, copy-semantics mutation pin, want 139), spread_place_kinds (str 24B headers / narrow i32 / empty deref source), spread_growth_place (40-elem spread crossing cap doublings), spread_selfalias_chain (source header inside the dst's grown buffer), and the two new loud-tail rejects (CALL rvalue, [N]T array) pinning the "#35:" diagnostic on both stages. 87 fixtures green (was 70), per-row cs/ww asm byte-cmp included. Unblocks regex fold-5a: add_thread regex.ha:569/572 + search ha:820 spreads go from loud-bound to real spread. |
|||
| 1bcf2726cf |
wcc+w6c+w6c_ww: delete() range form delete(xs[lo:hi]) (fold-5a P2)
Hare's delete also takes a slicing place (harec check.c:1981-2027 EXPR_SLICE; Hare spells it delete(xs[i..j])): remove [lo, hi) — shift [hi..len) down count = hi-lo strides, len -= count, cap unchanged; lo defaults 0, hi defaults len, so delete(xs[:]) clears the slice with storage retained. Checker accepts N_SLICE next to N_INDEX (object must chase to a slice, harec :2024); the old range-unimplemented reject and its #35 cite drop. Lowering (both stages, converged byte-identical by construction) is the single-element arm's same-slice whole-stride word-copy loop with a DYNAMIC src offset (count*esz via a src register) instead of the constant one-stride. Base shapes: local slice ident, deref-of-local, plus NEW indexed local-slice base xs[g][lo:hi] — the fold-5a consumer shape (regex.ha:333 delete(jump_idxs[group_level][..]); outer stride off the type table). Bounds stay implicit, inheriting the documented single-element posture (no index checks anywhere in cgen). Operands evaluate left-to-right, exactly once, before the shift (harec order); only the header ADDRESS is taken before operand eval, so a bound expression's writes through the slice land before the copy. test/809: 64 fixtures — full/explicit/re-clear/head/mid/tail/empty a:a/end-boundary len:len/explicit 0:0 on a never-appended (nil-ptr) slice, single-vs-range equivalence, cap preservation, esz 1/2/4/8/16/24 copy tails against the dynamic src, operand order-of-eval (lo/hi CALLs fire once each, in order) + aliasing-visibility pins, the EXACT [][]size regex consumer shape, deref base, 2 reject rows w/ diagnostic text; every accept row cs==ww asm byte-id. test/804: reject_range row retired (form now accepted), reject_nonindex text follows the widened message. |
|||
| 60e61315bc |
ww/lex: fold float literals through strconv.stof64 — 1-ULP cs≠ww class (#62)
wwstage's parsef64 (naive i64-accumulator + pow-10 fold) diverged from cstage's strtod: >19-digit mantissas overflowed the accumulator (sign-bit garbage), DBL_MIN was +1 ULP, DBL_MAX -2 ULP — the #59.10 ratchet pin. C-strtod oracle confirms cstage correctly rounded on every vector, so wwstage aligns to it by dogfooding strconv.stof64 (correctly-rounded decimal engine, already imported by lex.ww). Overflow literals now reject in both stages (stof64 overflow -> errat, mirroring ERANGE). Fix + #59.10 M_DIVERGE->M_ID graduation + pins land together per the ratchet's designed flow (the gate trips loud demanding graduation): oracle-pinned vectors in toktest.ww floatfold_cases (lexer-unit) and 989_floatlit_run (compiler fold: runtime bits + byte-id + overflow reject parity). Retained subnormal accept-set asymmetry filed as task #21, documented at the lexnum site. |
|||
| 74767c70cc |
wcc/check+wcc_ww/check: reject overlong array literal — frame-smash class (#71)
An array literal with more elements than the declared [N] passed the per-element accept-if-fits checks in both stages and cgen then stored every element at its natural offset, writing past the slot: local frames smashed silently (the repeat form [1,2,3...] into [2]int wrote at the saved BP), module DATA corrupted neighbours. All four declaration contexts (local let, module let, def, struct-field literal) funnel through one choke point per stage — arrlit_init_fits (check.c) / checkarrlitfits (check.ww) — which now pre-counts the literal (skipping the ... marker) and rejects count > N naming both counts. cstage clet's blanket has_arr_repeat bypass is narrowed to non-array declared targets: repeat literals into arrays now run the same overlong + #130 range checks wwstage's checkletassign always ran (the bypass let [2]u8 = [999...] dodge the range check cstage-only). checkarrlitfits also recurses into NESTED array-literal elements (declared elem node N_TARRAY): cstage catches the nested shape through its typed-literal assignability net, which wwstage's untyped elements have no analog of — [2][2]int = [[1,2,3],[4,5]] at module scope silently emitted corrupted DATA (1,2,4,5) and the struct-field twin likewise. Recursion through the one choke point closes any depth; a named-alias element type still bypasses — task #16. alen==0/nil-length stays exempt ([0]/[_] sentinel conflation and un-inferred [_] in def/struct-field — task #11); a non-INTLIT length child (def-named [N]) is exempt in wwstage — task #13; under-long literals keep their current accept (Hare rejects — task #10); wwstage's overlong accept at assign/call-arg/return position (cstage already rejects) is task #12; exact-fit bare-int nested cs-reject/ ww-accept divergence is pre-existing — task #17. |
|||
| 3daf134395 |
lib: retire os.assert/abort shims — assert/abort are builtins (#58 respell)
The flat checker scope makes ANY decl named assert/abort anywhere in the combined unit disable the builtin unit-wide (the #45 shadow shape: scope_lookup_prefer's cross-module fallback finds it). lib carried three colliding @symbol("rt_abort") shims (os, time, strconv/stof) plus the os.assert wrapper, so a bare assert(cond) in ANY program importing os mis-bound os.assert and failed arity — a hard blocker for regex fold-5 (regex.ha:660/670 bring builtin-assert mass). Ruled respell-now per the recurrence test (#45 -> #58). Delete the shims and the os.assert wrapper; every bare abort(msg) caller (regex, strings, utf8, hash, getopt, encoding/*, time, stof) now lands on the builtin, and the ~40 os.assert(c, m) sites respell to the builtin assert(c, m) — restoring the exact Hare spelling the lib ports diverged from (e.g. ref/hare/bytes/tokenize.ha:23). os.assert had no Hare counterpart (Hare's assert is a language builtin); rule-9 wrapper removed. temp/dirs/bufio already use the non-colliding rtabort spelling and keep it. Now-dead 'import os;' lines kept (pre-existing precedent: lib/strconv/strconv.ww carries one); a tree-wide dead-import sweep is a separate concern. regex.ww's if+abort workarounds citing #58 stay for the fold-5 owner to fold back into assert. combined.ww regenerated for all five selfhost tools + the smoke fixture via make. |
|||
| e091dfbdbe |
wcc-ww: assert/abort builtins — checker tag + cgen rt_abort lowering (#58)
wwstage had no EXPR_ASSERT-family intercept: the checker left bare assert/abort calls untyped (asserttyped gate 4 skipped them by design) and cgcall fell through to the regular call path, emitting CALL assert(SB) for a symbol that exists nowhere — link-fail. cstage was already correct (tag ty_err at check.c:1536-1572, lower inline via rt_abort at cgen.c:6618-6663). Mirror the same tag-then-lower pair: exprtype N_CALL stamps the call void and the callee TY_ERR behind the scopelookupprefer no-shadow gate (the isassertfam predicate), with the cstage arg diagnostics (cond must be bool, msg must be str, arity caps); cgcall keys on the TY_ERR tag and emits the identical CMPQ/JNE/rt_abort sequence. A user-shadowed assert/abort (same-module or cross-module, the #45 shape, task #14) stays untagged on the regular call path — byte-id for all existing lib code preserved. The cond check does NOT alias-peel: cstage compares ty_bool by identity (check.c:1560), so `type myb = bool` is rejected there; wwstage aligns down per rule 10 (a resolvealias here was accepting it — cs/ww accept-reject divergence). Widening both stages together belongs to the alias-peel choke-point arc (task #5, #47/#68). The resolvewalk N_IDENT resolution counter learns the builtin shape: an unshadowed abort/assert ident binds no sym BY DESIGN, so wwdump -r's zero-unresolved gate (990 probe 4) counts it resolved instead of failing builtin-using units. test 957: 13 rows — pass/fail/msg/bare abort (run exit + rt_abort stderr content; no-msg rows pin EMPTY stderr = the (NULL,0) shape), assert in an imported module, same-module + cross-module shadow controls, 5 checker rejects pinned on diagnostic CONTENT (shared substring; cstage prefixes pos, wwstage cerr is bare) incl. the alias-of-bool cond row pinning the rule-10 down-alignment; each positive row pins cstage run exit + cs==ww byte-id. On pre-fix master 11/13 rows trip (survivors = the two shadow controls). Residual (separate root, deferred diagnostic class): zero-arg assert() is not intercepted by either stage; cstage rejects via the generic undefined-ident path, wwstage's undefined-callee diagnostic is the class deferred behind wiring checkfile into w6c_ww. |
|||
| 33ec0fb1ac |
w6c+w6c_ww: (*p)[i] deref base materializes the array ADDRESS (#61 C)
Both stages SEGV'd identically (byte-id-blind): cgun's TK_STAR emitted a scalar MOVQ (AX),AX for an array pointee, so the index consumed a[0]'s VALUE as its base — a wild deref. An array value IS its address everywhere in this cgen (#270-1a), so the ARRAY pointee now takes the same skip as the #185 *fn deref in both stages: `*p` leaves AX = p's value, and every consumer that materializes a complex index base via cgexpr(base) — N_INDEX read fallback, cgassign store/compound, TK_AMP, N_SLICE — gets the array address from the one deref choke-point. wwstage additionally joins the N_UN-TK_STAR base to the stamped-tinfo esz arms (cgindex / cgassign store + compound / TK_AMP &(*p)[i]) where cstage reads base->type uniformly: without it, esz fell to the 8B default and a narrow element would mis-stride the moment the base started materializing (cs!=ww only reachable post-choke-point-fix, which is why it rides this commit). 949_ptrarr_index_run grows the deref_* rows: read (8B/4B/param-base), write (8B / 1B+neighbor-guards), compound — runtime + byte-id, the only nets that can see a both-stages-identical miscompile. |
|||
| eea3e197c2 |
w6c+w6c_ww: *[N]T indexing strides by element, not whole array (#61 A+B)
Indexing through a pointer-to-array auto-derefs, so esz and the element classification must come from the pointee array's ELEMENT (cstage idx_eff semantics, cgen.c:1163). Two halves of one root class: A (wwstage-only, cs!=ww, cstage runtime-correct): elemsizeofc's #270-2 nested-array block treated an N_TPTR pointee-array like a [N][M]T outer index and returned the whole-array size — every p[i] read/write/ compound scaled by N*size(T), and the same wrong element belief reached the store-width chooser (var-idx write emitted an N*8B aggregate copy sourced at the 8B rhs slot: caller-frame smash, the siphash round() corruption). Fixed via two wwstage choke-points mirroring idx_eff: idxeffti (tinfo: NAMED peel + TY_PTR->TY_ARRAY drill; feeds elemsizeofc and elemissignedc/elemisfloatc/elemisf32c) and idxelemtn (node: element tnode with the same drill; feeds every cgindex/cgassign/nodeisstr/ match-scrutinee elemtn resolution). B (BOTH stages identically wrong, byte-id-BLIND): the TK_AMP &base[i] arm read bu->sub->size without the ptr peel (&p[3]-&a[0] = 96, not 24). cstage now routes esz through idx_eff. A and B are FUSED by the pre-existing routing topology, not by choice (rule 11): wwstage's TK_AMP arm already reads its esz via elemsizeofc (selfhost/cmd/wcc/cgenexpr.ww:4095, the #11 addr-of twin of the #10 cgindex fix), so fixing A's choke-point flips wwstage's half of B in the same stroke. A standalone A leaves &p[i] transiently cs!=ww; B-first is the mirror transient; carving the TK_AMP caller out of the fixed choke-point to preserve the wrong stride for one commit would be a deliberate known-wrong intermediate (rule-7, vetoed by rob). One choke-point, two enrolled routes — un-fusable without a red intermediate. Close-by-construction proof-grep (both stages): every remaining raw sub->size index-stride read is TY_ARRAY-gated, a slice-only builtin (delete/insert), a checker-stamped element tinfo (indexresult already decays *[N]T, check.ww:2277-2284), or a non-index context (tuple slots, let-init elements). Two true residuals filed with site+symptom instead of silently absorbed: N_SLICE through *[N]T does not decay (LOUD type error, Hare divergence; team task #18) and non-ident cast-expression index bases keep wwstage's 8B-default esz (pre-existing #74-style cluster; team task #19). cstage's N_INDEX read-side str/slice header gates also move from u->sub to esub (identical for every non-ptr-to-array base; honest for *[N]str — pre-fix BOTH stages were runtime-wrong there, differently). 949_ptrarr_index_run pins the class at runtime + byte-id: {1,2,4,8}B elems, const+var idx, param/local/cast bases, read/write/compound, neighbor guards, &p[i] pointer-difference, siphash-round mix shape. 989_lib_byteid: siphash_test graduates #59.7 DIVERGE -> ID (ratchet tripped loud pre-update; no other #59.x pin flipped in the same run). (*p)[i] (sub-bug C) follows separately. |
|||
| 95fea97868 |
regex: fold 4 — bracket expressions (handle_bracket + run_thread charset arm)
Port of ref/hare/regex/regex.ha:135-225 (handle_bracket, whole),
265-275 (in_bracket dispatch), 313-314 (the `[` flip), 249-252 (the
bracket state quad) and 704-737 (the consuming charset arm). `[`
graduates from the fold-2a loud set; `(` `)` `{` are the last
three loud metachars. The POSIX-class arm keeps its DETECTION
verbatim but loud-aborts its BODY (charclass_map stays #25-blocked;
falling through to the literal arm would silently compile
[[:alpha:]] as a 9-literal charset). is_consuming_inst already
covered inst_charset.
Spelling divergences, all site-documented: the dispatch propagates
via the explicit D13 match, not `?` (compile's 64B sret return is
the #38b loud-stop; the fold-3 find_last_groupstart precedent);
charset's declaration moves BELOW its member types (cstage sizes a
tagged alias with forward-declared members at a degenerate 8B —
ww-core #69, wwstage is correct); run_thread binds the charset
structurally, not via the alias (alias-typed slice locals mis-scale
their index reads in wwstage — ww-core #68).
Tests: Hare's own bracket block (+test.ha:278-345, the group and
POSIX rows excluded with their loud arms) as the 72-row find/test
table incl. multibyte literal+range brackets and an unanchored
[ab]+ composition row; charsets-table content pins (lit/range
discrimination, first-char ]/[ literals, literal dashes, multibyte
codepoints); program-shape pins ([abc] / ^[abc]$ / [^ab] /
[ab][cd] / [abc]*); exact-text error rows (Unmatched '[' ×3 incl
the escape interaction, descending [z-a]); findall composition.
The [[:alpha:]] abort text is unpinnable in-process (it kills the
runner) — source-audited until the POSIX fold.
|
|||
| 0055ac2cd3 |
w6c+w6c_ww: for-range over a non-ident slice base — bound from len, base ptr spilled (#70)
The N_FORRANGE header's non-ident arm stored cgexpr's AX into the
single bound temp — but a slice-valued cgexpr leaves AX=ptr, BX=len,
CX=cap, so the loop compared i against the DATA POINTER; and the
per-iteration element address had no non-ident base arm at all, so
the bound reload doubled as the base. One slot, two roles, holding
the wrong word. An empty slice coincidentally exited (ptr==0), which
is how regex.finish's `for (let charset .. re.charsets)` — planted
verbatim in fold 1 — stayed latent until fold 4 produced the first
non-empty charsets and SEGV'd. Byte-id both stages (the 989 M_ID
entry held on both-wrong-identical); first-consumer surfacing, the
kwtab/#8 pattern.
Fix mirrors the correct local-base arm: bound = BX (len), base ptr
spilled to a dedicated .rgb slot and reloaded per iteration. Covers
field-chain, indexed-element (the task-#57 shape) and call-result
bases. Two shapes whose cgexpr does NOT deliver the header convention
stay LOUD instead of silently wrong (rule 7): deref bases (*p — the
#11 deref-spine family) and non-ident ARRAY bases.
test/937: field (value+ptr roots), 24B-str-header field (the finish
shape), indexed, call, empty-header, eval-once (header captured at
loop entry, not re-read per iteration) rows + the two reject pins,
per-row cs==ww byte-id; verified failing 14/22 at the #66 parent
|
|||
| bb8a44a564 |
w6c+w6c_ww: cast-wrapped tuple literal widens its whole payload into a tagged slot (#66)
The #242 tuple arm of the widen choke-point (cg_widen_tagged_store /
cgwidentaggedstorebp) gated on a BARE N_TUPLE source. The cast-to-
CONCRETE-VARIANT wrapper ((a, b): range_alias) — the only spelling
real code uses (ref/hare/regex/regex.ha:213) — is not a widen-cast
(its destination is the variant, not the union), so the peel left it
intact and it fell to the SCALAR arm: cursor word 0 stored, payload
slot 1+ silently zero-filled. Both stages, byte-id, gate-blind.
Fix at the choke-point: peel N_CAST(lhs=N_TUPLE) where the NAMED-
peeled cast type is TY_TUPLE and iterate the inner element list; the
variant tag keeps resolving from the CAST's type (exact named match),
so the #241 untyped-element loud-stop stays scoped to the bare form
on both stages.
Closure by construction needed two more arms (reviewer proof-grep):
cg_widen_tagged_push's direct-push fast path classified a tuple-typed
ARG source as scalar — pushed word 0 only AND coerced an unresolved
tag to 0 — so f(((a,b): rng)) bypassed the fixed arm entirely (and
the bare typed (a,b) arg dropped slot 1 the same way). Tuple-typed
sources now route through the scratch store. The remaining non-
literal tuple sources (ident / call result / match binding) have no
word-copy arm in the store and fell to its scalar arm — loud-stop
(rule 7) until #72 wires them. Every tagged-payload materialisation
now funnels through cg_widen_tagged_store, which handles or rejects
every tuple shape: let/assign/return/append (cgen.c:7511) directly,
arg push via the scratch route.
test/936: cast-tuple matrix (let / append local+index-place+deref-
place+ptr-field-place / ident+float+str elements / 3-member layout-neutrality /
direct-arg) + bare-form no-regress (return + arg) + bare-literal and
tuple-ident reject pins, per-row cs==ww byte-id; verified failing
24/40 at parent
|
|||
| 8578ad0533 |
wcc+w6c+w6c_ww: global tuple lets — DATA emit + element reads + len(g.N) (C-t3, #48)
Global tuple lets were WHOLLY unwired, silently: let_emit_size returned 0 so emit_lets SKIPPED the definition (no DATA, no diagnostic), then cstage's t.N read and #235 len arm read BP-frame garbage (localfind→0) while wwstage — with the tuple never in collectlets — mis-emitted the field index as a symbol (`MOVQ 0(SB), AX`). ken's #48 was the len() facet of this. Now: let_emit_size/letemitsize admit TY_TUPLE (slot-sum size, rides C-t0); emit_tuple_data/emittupledata lay the slot-format DATAW row — a scalar element one 8B LE word, a str element its 24B header slot with a DATAR ptr patch at the element's slot offset (the #18 [N]str per-element pattern; strlits pre-interned in element order) — and any element that doesn't reduce to an int/str literal dies LOUD instead of skipped. The t.N read and len arms gain the global base (LEAQ sym(SB) into CX, the struct-field-global pattern; wwstage's C5 len loud-stop graduates to the working path). A GLOBAL tuple as a first-class VALUE (`let q = g;`) loud-stops on both stages — pre-fix it byte-identically loaded word0 only and read a stale cursor for words 1+ (element reads are the supported surface). 941 grows the t3 rows: global element reads (str+i64 and packed u32,u32 incl. len(g.0)) + rejects (float-element init, whole-value use, pre-existing element-write anchor). 7/82 checks fail at the C-t2 parent (cs silent-garbage runtime, ww C5 build-fail, both rejects vacuous-or-absent). |
|||
| 6426fac6f2 |
w6c+w6c_ww: tuple by-value ARG send — every cursor-filling producer rides #163 (C-t2, #32)
node_tuplearg was N_CALL-scoped and its comment claimed non-call forms "loud-stop" — they did NOT: a tuple ident/literal/unwrap arg fell to the scalar single-PUSHQ default, skewing every later arg register so the callee read garbage word 2 (byte-id both stages, the gate-blind both-wrong class; packed shapes SIGSEGV'd pre-C-t0). The receive side (cgfn #163 walk) was already correct. cgexpr already fills the return-ABI cursor for every supported producer (#241: ident via slot-to-cursor, literal via lit-to-cursor, unwrap via payload shift; call via the return ABI) — the send now admits exactly those into the existing @tupargscr restage + per-class drain (node_tuplearg widened; wwstage gains nodetuplearg, mirroring it over the local tnode / inferletcalltype; rettupleof stays N_CALL-scoped for the destructure receives). Any OTHER tuple-typed source shape loud-stops at the push site — the false comment's claim, now true (rule 7). Literal tuple elements are stamped expr types, so the restage/drain wide test goes type_isstr/type_isslice (TY_UNTYPED_STR- aware) with the ty_str->size header stride; the wwstage twin walks a literal's VALUE exprs the way cgtuplelittocursor classifies them. Ken review demands folded in: (1) a NESTED composite element (tuple/struct/array/tagged inside the tuple) occupies more than the one GP word the restage walk counts — the checker accepted it and it ran WRONG (inner words skewed, wwstage SIGSEGV); both stages' restage walks now loud-stop the element kind (wiring is the filed follow-up, task #65). (2) the variadic interaction probed: a tuple arg ahead of a variadic tail rides the restage correctly (positive row); variadic-of-tuples stays bounded-loud via the tuple-in-slice read surface. 941 grows the t2 matrix: packed/16B params with branched callees, mixed arg orders both ways, literal arg, (f64,i64) param, unwrap arg, ken's >6-GP-pressure stress (4 leading scalars + tuple + a 7th stack-class word), variadic-after-tuple, plus rule-7 reject rows (chain-source arg, nested-element arg, variadic-of-tuples, over-cap ident arg) and the fold-4 charset substrate pin ([](u32,u32) append stays LOUD). At the C-t1 parent 18/73 checks fail: every runtime arg row except the (f64,i64) anchor on BOTH stages (byte-identically — the gate-blind both-wrong class) and the chain/nested args silently accepted. |
|||
| 12af54f9f8 |
w6c+w6c_ww: tuple-let receive keyed on type classify, not producer shape (C-t1, #33)
wwstage cglet's tuple receive was producer-SHAPE-keyed: the mixed str/scalar arm required s0_is_str != s1_is_str (syntactic) and the rt16 arm required an N_CALL rhs (rettupleof), so a scalar-scalar tuple LITERAL `let t: (u32,u32) = (3,4)` matched neither and fell to the generic single-word store — word 1 silently dropped (#209/#211-class syntactic-vs-type keying). cstage's twin arm was sz==16/32 magic-size keyed, so 24B 3-scalar tuples dropped words 2+ on BOTH sources. Both stages now key the same way: declared-type TY_TUPLE + in-cap register classify (cg_sret_retsize / sretretsize == 0, the shared SSoT), alias-peeled; the two wwstage shape arms collapse into one type-keyed arm walking the declared element list (the #240 lesson — never the producer's). Over-cap falls through to the sret receive exactly as before; unannotated `let t = f()` rides inferletcalltype. 941 grows the t1 rows: lit packed/16B/3-scalar + call 3-scalar fail at the C-t0 parent (10/39 checks — wwstage lit halves AND both-stage 24B halves), mixed-lit + unannotated-call anchor the untouched paths. Filed while probing: cstage silently accepts an over-cap tuple-LITERAL let where wwstage loud-stops (pre-existing at master, task #64). |
|||
| fdfc2ce318 |
wcc+w6c+w6c_ww: tuple slot layout SSoT — checker size = cgen slot stride (C-t0)
The checker computed TY_TUPLE size as the packed element-size sum ((u32,u32) = 8B) while every cgen cursor-transport site strode 8B slots (16B). 16B tuples were blind to the split (slot == packed); packed tuples hit it everywhere: cstage let-receive keyed on sz 16/32 missed sz 8 and dropped word 1, the cgfn param receive spilled 8B/element into a packed-sized local (saved-BP clobber, SIGSEGV), and mixed (u32,f64)/(u32,str) shapes missed the receive arms entirely. Slot layout is now the SSoT (user-ratified): the flip lives in the two checkers' N_TTUPLE size computation only (check.c, check.ww tupleelemslot + stamp); cgen's packed-keyed walks (t.N read, #235 len arm, over-cap sret send/receive pair) align onto the slot stride, and the wwstage t.N read gains the natural-width load (tnodeloadop) to byte-id with cstage's fldloadop. ttupleelem.offset re-stamped slot-cumulative (no consumers yet). The #242/#243 eightbyte-share loud-stop dissolves by construction (no two narrows ever share an eightbyte) — 940's eightbyte_share row graduates to a runtime round-trip. Hare-layout divergence documented at both checker sites; re-alignment is task #60. #32 send skew and #33 wwstage literal-let receive are separate commits on this base. 941_tuple_slot_layout_run pins the matrix: 4 packed rows fail at the parent (8/21 checks), 3 neutral anchors prove 16B/32B emission untouched. |
|||
| 0139652180 |
test/989: lib byte-id gate — w6c vs w6c_ww over every non-embedded lib unit
The 990-997 gates byte-id only the selfhost-embedded modules; every other lib/ module compiled cstage-only, which let regex.finish ship cs≠ww for weeks (task #21, FC0). 989_lib_byteid compiles each lib test fixture's resolved unit (plus import-probe stubs for the fixtureless sort/path/endian/net/hash/fnv/crypto.math/c.libc) through both stages and byte-compares the asm: 28 units pinned byte-identical (incl. lib/regex), 12 known divergences + 3 wwstage front-end rejects pinned as documented-allowed with task #59 cites — a landed fix trips the pin and demands graduation, so the corpus can only ratchet toward ID. Two rot-guards, both review-driven: each probe carries a sentinel that must appear in the resolved unit (the driver silently skips an unresolvable import, so a dropped probe would byte-id an empty main — green while covering nothing), and a corpus-completeness scan fails loudly on any lib/ dir not enrolled, so new modules cannot ship uncovered. Compile+cmp only (no driver run, no source-tree writes): phase-1 parallel-safe, ~6s. |
|||
| da48b29a7c |
lib/regex: fold-3 — anchors, escape, postfix ?/*/+, alternation
Ports compile()'s fold-3 arms (ref/hare/regex/regex.ha): \ escape
(286-293), ^ (294-300), $ (301-312, goes live with run_thread's
anchored ha:621-624 pin), | (335-367) over the restored jump_idxs
prologue (241-256 subset: jump_idxs + was_prev_rune_pipe +
group_level verbatim-but-0) + the whole-expression fixup (470-473,
the SIZE_MAX-sentinel overwrite 2a dropped), ? (403-420), * (421-443),
+ (444-459); find_last_groupstart (104-119, whole — error arm is the
live one until the group fold) and shift (123-133). run_thread's
inst_split/inst_jump aborts FLIP live (606-611, all 2b-proven
shapes). Still loud: [ ( ) { in compile; groupstart/groupend/repeat/
charset arms; add_thread's capture-dup bound.
Spelling divergences, each cited at site:
- multi-type case (inst_lit | inst_charset | inst_any) (ha:407) is
loud-rejected both stages (PE5; Hare-parity #13) -> three void arms.
- find_last_groupstart(...)? in the dead groupend arms hits the #38b
>32B-tagged-return propagate loud-stop -> explicit D13 match
(harec's own ? desugaring).
- by-value range over an indexed element (for (let x .. jump_idxs[g]))
SEGFAULTS both stages (NEW, filed ww-core #57) -> D9 index loops at
ha:351-356 + 470-473.
- assert(cond, msg) is wwstage-broken (CALL assert(SB), undefined at
link; NEW, filed ww-core #58) -> if+abort at the ha:355 site.
- Hare's match/if EXPRESSIONS (ha:337-346) -> statement spellings
(the #51 search/scanrune precedent).
Tests: the 11-pattern loud row shrinks to the 4 remaining metachars;
strerror reroutes through a[. New: fold3_programs (exact inst
sequences incl. the a|b sentinel fixup), fold3_compile_errors (12
exact texts incl. Hare's own ab\|^cd ERROR fixture), direct
find_last_groupstart/shift rows, fold3_find_cases (42 rows from
Hare's +test.ha anchors/postfix/alternation blocks, group-free
subset, end=-1 resolved to rune-length; rob's dedup/leftmost/longest
riders now observable: a* over aaaa -> single (0,4), b+ over abab ->
(1,2), b* / ^b* over aaaabbbb -> (4,8)/(0,0); multibyte b+ row keeps
idx != bytesize) + the a*-over-baa findall rider (longest-pick beats
the zero-length candidate; trailing zero-length match takes the
ha:942-945 break). Both drivers green; regex_test.combined.ww cs==ww
byte-identical.
|