Commit Graph

97 Commits

Author SHA1 Message Date
c7d9dc92de selfhost: migrate tool sources to directory packages
Build w6a and w6l from package-main directories and expose the wcc backend through a narrow package API so w6c and wwdump no longer import implementation files. Retarget the remaining load-bearing fixtures and example sources to directory packages; retain the one intentional flat compiler collision as an explicitly composed raw unit.
2026-08-12 17:12:03 +09:00
db96422f74 compiler: load imports and enforce package exports 2026-08-11 22:25:59 +09:00
a04de3e02b bufio: make scanning bounded and incremental 2026-08-09 18:17:42 +09:00
134f0ceef0 test: delete unconsumed legacy inputs 2026-08-09 12:07:13 +09:00
53a9e94736 wcc: reject final-band integer overflow; restore rewind columns 2026-08-09 03:25:17 +09:00
999b110001 wcc: forrange bindings get a per-loop scope; dup binders reject
The TODO(#11) silent-accept of `for (let (a, a) .. xs)` rested on a
stale premise -- resolvewalk has per-block scopes since #53 and IS
the live w6c_ww checker pass. Bindings now install in a per-LOOP
scope (the N_MCASE pattern), so a duplicate name within one pattern
errs "redeclared in same scope" (cstage parity via stamptuplebinds'
scopedefine-nil check) while sequential same-name loops stay legal.
kwtab restores its stated alphabetical order (`import` before `is`,
both stages, kinds swapped in lockstep with names).
2026-08-09 01:59:33 +09:00
411515a83b wcc: checker-seam drain — spreads, tuple aliases, yield scoping
Four seams from one review cluster, all landing on the same checker
files; gates ran on the union (rule 11 body).

?-subset: the wwstage walk compared `...spread` ALIAS nodes on both
sides of the error-subset check, falsely rejecting a spread-carried
error return (`(i64 | ...errs)`, cs-accept/ww-reject) — both sides
now flatten like trycountvariants (cstage Tparams are pre-flattened
at type level).

Multi-let/multi-assign: the tuple gates keyed on the RAW rhs kind,
rejecting a NAMED tuple alias (`type pair = (i64,i64)`; cs clean-
reject, ww asserttyped stop) — both stages chase per #99 alias
transparency, wwstage rettupleof peels the alias so cgmassign keeps
the str element's len/cap stores, and the catA massign-alias error
pin re-rules to a run fixture. The N_MLET diagnostic also printed
its operands swapped (elem/declared reversed vs its wording).

Yield: match_yield_type didn't descend into N_SWITCH, so a yield
inside a switch arm typed the match void and dropped the value
(both stages); and a stray yield outside any match arm reached cgen
unchecked — both stages now gate on a match-arm counter (the
c->loops discipline).
2026-08-09 01:51:39 +09:00
503d1ab01e wcc: wwstage checks call arity at desugarcallargs
cstage rejects `add(1,2,3)` and `add(4)` (too many / not enough
arguments); wwstage ran no count check at this seam, so both built
-- a stray arg pushed silently, a missing one read garbage
(cs-reject/ww-accept build-verdict divergence). Surplus errs when
params exhaust with args left; missing errs on a leftover regular
param (a leftover TK_ELLIPSIS or FFI "..." is a legal zero-arg
variadic tail). fn-VALUE callees still bail at decl==nil -- their
whole typecheck, arity included, stays task #51.
2026-08-09 01:33:14 +09:00
cf9d83b209 wcc: enums compare nominally; folded members keep their enum stamp
Two composing defects made a two-enum union mis-tag in wwstage
(live cs!=ww: `let e: (color|shape) = shape.BALL` stored tag 0 —
the color arm — while cstage stored 1). type_eq/typeeq had no
TY_ENUM arm, so ANY two enums fell into the primitive default and
compared equal; enums are nominal (harec: an enum IS its alias
type) and now compare by node identity only. Underneath, the
wwstage post-order revisit re-stamped the constant-folded enum
member (an N_INTLIT) as untyped_int, clobbering the enum stamp the
N_DOT fold applied, so the widen matcher fell to its first-variant
fallback -- the #59.9 N_BIN guard now twins on N_INTLIT (cstage
cexpr is single-pass and never clobbered).
2026-08-09 01:26:31 +09:00
f06c95e66b w6c: forrange bind cap loud; drain widen gates keyed on type-eq (#55)
Two silent seams in one sweep. The for-range destructure silently
dropped the 9th+ binding in both stages (bind arrays are sized 8);
the cap now hard-stops per the DEFER_MAX/LOOP_MAX discipline (the
shape is unreachable today -- every wide-tuple construction path
already loud-stops). argtaggedwidensz, the drain-side SSoT for
widened call args, still keyed "natural push" on slot-size equality
while pushargsrev flipped to type equality with #55 -- a same-slot
subset was classified widen by push, natural by drain (counts
coincide today; a classification reader desyncs, the #48 shape).
Both sides now share the wsame key; the widen-branch tag miss for a
concrete source joins the task-2 loud-stop family (tagged subset
sources keep t=0 -- their widentag is never read, the scratch store
remaps).

Lead 17 (enum fold-failure prev+1) DISPROVED: enumvalfold gates
every member init in the checker and its fold set is op-for-op
equal to cgen's enumevalmember, so the fallback only runs inside
already-failing compiles.
2026-08-09 00:52:27 +09:00
973dd964c9 wcc: structlit repeat-fill dim off the stamped tinfo (rule 13)
cgstructlitfill's `...` total read only an N_INTLIT dim node, so a
def-dimensioned [N]T struct field under-filled: elements 1..N-1 kept
the zero-fill, silently, wwstage only (cstage and the tn twin read
the stamped array length). Read tichase(fi.tnode.type_).alen first;
the surface node stays as the fallback for unstamped shapes.
2026-08-09 00:40:23 +09:00
f191e6e0e2 wcc: modulo is integer-only; compound ops carry their operand class
Hare's rule (harec check.c binarithm): % and the bitwise/shift five
are integer-only; + - * / need numeric operands. ww grouped % with
the numeric ops, and compound assigns never op-checked at all, so
`a % b` on floats compiled half-lowered (live cs!=ww divergence),
`a %= 2.0` plain-stored the rhs (op silently dropped, both stages),
and `s += "cd"` garbled str headers. Gate both at the checker, both
stages; the cgen float-compound fallbacks and the three unknown-
compound legacy defaults (deref/global/local) demote to rule-7 hard
stops. 34 compound-on-tagged/str/slice fixtures re-pin from the old
cgen "not wired" stops to the earlier checker diagnostics; 3 new
reject fixtures pin the closed shapes.
2026-08-09 00:32:41 +09:00
94928470bc w6c: sret-wire the chained-leaf call store (o.a.b = wide())
The chained value-struct walker's N_CALL arm handled only <=24B
register returns; an sret-class rhs fell to the scalar tail and
stored ONE word (the sret dest pointer) into the leaf -- silent
truncation in BOTH stages, byteid-blind. Point the callee's hidden
RDI at the BP-relative leaf slot (the single-dot #234 arm verbatim);
a ptr-root/global chain joins the #234-tail loud-stop family.
2026-08-08 23:51:17 +09:00
83f5956df2 test: banner purge + WHY-only comment sweep (rule 8)
Every section banner dies (103 -> 0) across test/lang, the observer
suites, the C carriers, and the five comment-heavy corpus fixtures;
banner provenance (#N cites, carrier numbers, repair-cluster labels)
folded into headers or adjacent WHY comments. Narration deleted; row
provenance, ref cites, divergence pins, and layout contracts kept
(fwd-ref decl-order guards and bootstrap-gate corpus rationale
restored where the sweep over-cut). Comment-only proven: all 3742
wwbuild workdir .s byte-identical before/after; test-commit and
test-byteid (161 lang + 1399 data, 0 pinned-divergent) green.
2026-08-08 21:40:23 +09:00
659e859f34 ww: test sources are *_test.ww only (Go contract)
Go compiles only _test.go files as tests; discovery now keys on the
_test.ww suffix alone. The line-leading-@test compatibility allowance
(noncanonical filenames admitted as test sources) is removed from both
driver stages and the coordinator. An @test declaration outside a
*_test.ww file is rejected loudly ("@test declaration outside
*_test.ww", wording byte-identical cs/ww) instead of silently running
under compose or silently dropping in a non-T build (#6). Tree audit
found zero real carriers; the two allowance fixtures flip canonical
(dep_test.ww, widget_test.ww). New pins: direnum attest-noncanon
reject row (both-stage stderr parity) and the coordinator
noncanonical_attest_rejected package row.
2026-08-08 20:38:02 +09:00
241f28c96b syntax: align for-header grammar to cstage (Go's three forms)
The bbdd8bed residue, ruled by the Go derivation (ww's for IS the Go
for; the cstage grammar is the language definition and already the
Go-shaped one):
- bare `for { }` accepted (align UP to parse.c:1023, incl. for-else);
- 3-clause cond and post each omissible — `for (init;; post)`,
  `for (init; cond)`, `for (init;)` (align UP, parse.c:1092-1097);
- 2-clause `for (cond; post)` REJECTED (align DOWN): the form exists
  in neither Go nor cstage (parse.c:1101 eats a stray ';' then
  requires ')'), so the old wwstage accept was a parser bug. The
  reject wording mirrors cstage's failure mode per stage.

forhdr_{brace,emptymid,partial} run fixtures pin the accepted forms
byte-identically; forhdr_twoclause_reject pins both reject fragments.
Corpus pin 1745/346/21/209/1169/3490.
2026-08-08 19:19:42 +09:00
b52f30a894 wcc: reject module-scope alloc/call let initializers at check time
A module-scope let whose rhs runs code (alloc, call — peeled through
cast/?/! wrappers) emitted no DATAW slot: emit_lets' fold-fail
silently skipped the definition and every reference died at LINK
time with 'undefined reference', the one unacceptable failure mode
(rule 7). Hare's model rejects at check time (ref/harec/src/
check.c:4360 'Unable to evaluate initializer at compile time') and
routes runtime init through @init, which ww does not have — so both
frontends now reject at the declaration with identical wording.

alias_infptr_global flips compile->error as the alloc pin (its
letvartnode N_TPTR-over-N_TSTRUCT coverage lives on in the local
alias_infptr_{nest,slicecap} siblings); callinit_global_reject pins
the call shape. Corpus pin 1741/345/21/209/1166/3482.
2026-08-08 19:15:10 +09:00
cf69cf2f06 w6c: key tagged natural-push call args on type equality, not slot size
pushargsrev's five aistagged gates (N_IDENT #55, N_CALL #21, N_INDEX
#12, N_DOT #22a, deref #35) treated a tagged arg as already-tagged
when its SLOT SIZE matched the param's. A same-slot subset union
((bool|void) into (i64|bool|void), both 16B) then natural-pushed the
narrower box's words carrying SOURCE tags — no re-layout, no
cg_widen_tag_remap twin — so the callee matched the wrong arm
(silent: probes exited 10/90 where cstage exits 30/27). cstage keys
widen detection on type equality (cgen.c:10000 same = (pu == au) ||
type_eq) and routes every non-same tagged source through the zeroed
scratch + tag remap; the slot-DIFFER wwstage path already mirrored
that byte-identically, so the fix computes cstage's same check once
(wsame) and replaces each slot-size test with it. This also erases
the last known cs!=ww shape divergence (the 16B-local staging vs
direct-push frame delta on prefix subsets).

8 subsetwiden_* fixtures own the class: call-result/ident/str-payload
/mid-arg remap (the wrong-arm shapes), prefix (the shape-divergence
repro), and bigslot/return-pos/struct-24-to-32 sibling guards. Corpus
pin 1740/343/22/209/1166/3480.
2026-08-08 19:01:05 +09:00
184afa3243 syntax+w6c: multi-type match patterns in wwstage (align up to cstage)
`case T1 | T2 =>` (binding-less; cstage parse.c:635-650 chains extra
types through cs->list, bindings stay single-type by design). Three
wwstage layers, no AST change (node.list already exists):
- parser (expr.ww): pipe loop after the non-let arm's first
  parsetype;
- checker (check.ww): resolvewalk N_MCASE now walks n.list so each
  alt gets its type_ stamp (exhaustiveness/casecovers were already
  alt-aware, built ahead of the parser);
- cgen (cgenexpr.ww): the single-pattern want-computation moves
  VERBATIM into matcharmwant; a cs.list arm emits CMPQ/JE per alt
  funneling into one match_body label, mirroring cgen.c:11118-11141
  incl. label mint order and the tag<0 clamp. Nullable arms keep
  ignoring alts (both stages).

Graduates the held e2e row (pin 1732/1158/3464); byte-identical on
the repro and the single-pattern control.
2026-08-08 17:15:41 +09:00
bbdd8bede6 syntax: accept the empty for () condition (align up to cstage)
cstage parse.c:1027 accepts `for ()` — N_FOR with a nil cond, no
for-else arm, no test emitted (an unconditional loop; a deliberate
Go-flavored ww divergence from Hare, original to the toolchain
import). The wwstage parser had no RPAREN early-out and rejected it.
Selfhost checker and cgen already nil-guard n.cond, so the parser arm
is the whole gap. Graduates the held e2e row (pin 1731/1157/3462).

Adjacent asymmetries left filed, not fixed here (rule 11): the brace
form `for { }`, the empty middle clause of the 3-clause form, and
the REVERSE case (wwstage accepts 2-clause `for (cond; post)` which
cstage rejects).
2026-08-08 17:15:20 +09:00
d78137a051 w6c: fold dot chains through inferred-alloc struct pointers (#24 residue)
wwstage dotchainresolve gated its *T roots on an N_TPTR-over-N_TNAME
tnode; an inferred `let p = alloc(S{...})!` rides the checker-
SYNTHESIZED N_TPTR whose pointee is the struct BODY node (the #24
TNAME-normalize covers only the direct struct-lit binding), so the
root gate failed and chained p.field.pseudo reads fell to the
unfused deref arms — shape-only divergence vs cstage's type-keyed
fold (both stages runtime-correct; the historical field(SB) leak the
pin described was already fixed). Widen the local and global root
gates to accept the N_TSTRUCT pointee; correctness stays enforced by
the stamped-tinfo peel below (TY_PTR -> pointee TY_STRUCT).

Graduates alias_g73_heapfill out of DATABYTEID_DIVERGED (2 pins
remain, both the tagged-spill family); adds compile fixtures pinning
the newly-converged siblings (nested read+store, slice .cap,
inferred-global root; pin 1730/22/3460) and a runtime lang row that
mutates then re-points the heap base — a read bypassing the pointer
returns the wrong len.
2026-08-08 17:14:59 +09:00
dc33af6217 w6c: remap legacy-union error tags in wwstage ? propagation
The #173 remap loop in cgtryprop gated on the tparam's explicit `!`
flag only; a LEGACY union (no marks anywhere) classifies error
variants POSITIONALLY (index 0 is success, cstage cg_variant_is_error
cgen.c:905) — so wwstage emitted no remap and propagated the callee's
raw tag into a differently-ordered caller union, a silent wrong arm
(cstage exit 7, wwstage 9 on the banked r700 row). Add the
variantiserror twin and gate the loop on it; refresh the drifted
cstage cite.

Graduates the held e2e row into the corpus (pin 1727/1156/3454) and
adds legacy-mode rows to test/lang/tryprop_tag_remap_test.ww:
reversed order both directions, slot-0 remap, str payload integrity,
a two-hop chain whose pre-fix runtime pass was double-miss tag
cancellation (the lang byteid leg pins the emitted remap blocks),
and a same-order zero-emission control.
2026-08-08 17:11:50 +09:00
934f19f884 test: own the 926/929/671 run legs as corpus fixtures
The three carriers retired this session moved their compile/byte-id/
asm-marker legs to test/asm/tagsret_test.ww, test/asm/memarg_test.ww,
and test/misc/packedwwi_test.ww, and audit-assigned their run-exit
legs to corpus fixtures that were never created. Create them, sources
byte-faithful to the carriers' row tables; every fixture also rides
the corpus byte-id sweep, which stayed green (no new DATABYTEID pin).

926_tagged_sret_run.c run rows (all exit 0):
  wide_lit_roundtrip          -> r926_sret_wide_lit_roundtrip
  wide_local_roundtrip        -> r926_sret_wide_local_roundtrip
  wide_assign_receive         -> r926_sret_wide_assign_receive
  wide_match_scrutinee        -> r926_sret_wide_match_scrutinee
  wide_forward_exact          -> r926_sret_wide_forward_exact
  wide_str_error_variant      -> r926_sret_wide_str_error_variant
  wide_multicall              -> r926_sret_wide_multicall
  wide_bare_return_void       -> r926_sret_wide_bare_return_void
  wide_s4_repro               -> r926_sret_wide_s4_repro
  errno_shaped_tail_read      -> r926_sret_errno_shaped_tail_read
  boundary_s3_register        -> r926_sret_boundary_s3_register
  boundary_str_nomem_register -> r926_sret_boundary_str_nomem_register
  wide_discard_stmt           -> r926_sret_wide_discard_stmt
(the seven buildfail rows stay with r926_sret_reject_*.)

929_tagged_memarg_run.c run rows (exit 0 unless noted):
  mem56_ident_allvariants  -> r929_memarg_mem56_ident_allvariants
  mem64_ident_lateword     -> r929_memarg_mem64_ident_lateword
  mem56_widen_concrete     -> r929_memarg_mem56_widen_concrete
  mem56_subset_remap       -> r929_memarg_mem56_subset_remap
  mem56_srcshapes          -> r929_memarg_mem56_srcshapes
  mem56_mixed_orders       -> r929_memarg_mem56_mixed_orders
  mem56_call_in_loop       -> r929_memarg_mem56_call_in_loop
  mem56_twomem             -> r929_memarg_mem56_twomem
  mem56_inst_shape         -> r929_memarg_mem56_inst_shape
  mem56_elem_source        -> r929_memarg_mem56_elem_source
  mem56_elem_deref_spine   -> r929_memarg_mem56_elem_deref_spine
  mem56_elem_nested_spine  -> r929_memarg_mem56_elem_nested_spine
  mem56_elem_mixed_regs    -> r929_memarg_mem56_elem_mixed_regs
  mem56_elem_callidx_twomem -> r929_memarg_mem56_elem_callidx_twomem
  mem56_elem_global_slice  -> r929_memarg_mem56_elem_global_slice
  boundary48_register      -> r929_memarg_boundary48_register
  global_uninit_memarg_src -> r929_memarg_global_uninit_memarg_src (91)
  memarg_idcast_peeled     -> r929_memarg_idcast_peeled
(the four buildfail rows stay with r929_memarg_reject_*.)

671_struct_packed.c value rows (exit = layout value):
  unpacked_size    -> r671_unpacked_size (16)
  packed_size      -> r671_packed_size (9)
  packed_off_b     -> r671_packed_off_b (1)
  unpacked_off_b   -> r671_unpacked_off_b (8)
  packed_align     -> r671_packed_align (8)
  packed5_size     -> r671_packed5_size (5)
  packed5_off_b    -> r671_packed5_off_b (1)
  p3_packed_size   -> r671_p3_packed_size (4)
  p3_packed_off_c  -> r671_p3_packed_off_c (3)
  p3_unpacked_size -> r671_p3_unpacked_size (6)
  field_roundtrip  -> r671_field_roundtrip (6)
  arr_stride_size  -> r671_arr_stride_size (15)
  arr_stride_run   -> r671_arr_stride_run (33)
  byval_arg        -> r671_byval_arg (107)
  frame_offset     -> r671_frame_offset (20)
Owner-cited, no fixture: wwi_roundtrip needs a real multi-file -I
tree (a single-file two-package form emits no .wwi), owned by
test/misc/packedwwi_test.ww wwiroundtrip (exit 91 on both drivers);
identity_reject is stage-asymmetric, owned there as identityreject.

Pins: corpuscount 1680 -> 1726, runexitcount 1109 -> 1155,
nativecount 3360 -> 3452, corpushash refreshed.
2026-08-08 15:14:25 +09:00
90b1005478 test: point attest_userrun comment at the ported collide owner 2026-08-08 14:34:59 +09:00
89a4b82ba3 test: port 944_alias_cgen_b5_run to a compile fixture
944_alias_cgen_b5_run.c -> alias_g73_heapfill (//ww:compile), joining
its migrated alias_* siblings. The carrier's stated fixture blocker
('//ww:compile links') is stale: the COMPILE cell runs the bare
frontend, no link. The live #24 field(SB) deref-read leak enters
DATABYTEID_DIVERGED, upgrading the carrier's silent byte-id waiver
with a graduation tripwire.
2026-08-08 14:33:23 +09:00
a4839d0176 test: port 915/949 module-global access carriers to corpus fixtures
915_arr_module_index_run.c -> r915_arr_mod_idx0 (run), r915_arr_mod_
  idx2 (run-exit 1), r915_arr_mod_idx4 (run-exit 6); the single-file
  rows were already owned by r915_arr_local_* + test-data-byteid.
949_xmod_fnptr_const_run.c -> r949_xmod_fnptr_const_table (run),
  r949_xmod_fnptr_wrong_sig, r949_xmod_fnptr_nonfn_dotted (error
  'not assignable' both stages).

The 915 module rows upgrade from 994-delegated to per-fixture cs==ww
byte identity; 949's 2-file -I tree was not load-bearing — inline
packages exercise the same N_DOT &mod.fn mangle (mafn(leaf, module)).
2026-08-08 14:33:23 +09:00
5a8b662d6e test: port 839_xmod_nominal_typeeqast to corpus fixtures
839_xmod_nominal_typeeqast.c -> r839_xmod_nominal_match (run-exit 42),
  r839_xmod_a6_concrete (run), r839_xmod_incompat_concrete (error
  'not assignable'), r839_xmod_distinct_alias (error 'ambiguous
  without nominal layout').

r839_xmod_nominal_match enters DATABYTEID_DIVERGED: the carrier waived
byte-id for the pre-existing cross-module tagged-return spill
divergence; the DIVERGED row makes it loud-on-fix instead. The
typeeqast-layer discriminator itself stays with the lib byteid gate
(test/byteid/libbyteid_test.ww), as the carrier documented.
2026-08-08 14:33:23 +09:00
2680fba303 test: port 787/929 cross-module match carriers to corpus fixtures
787_xmod_variant_match.c -> r787_xmod_variant_no_default (run-exit 21),
  r787_xmod_variant_bind_default (42), r787_xmod_variant_shape_a (12)
  (pkg/errs module dirs), r787_xmod_variant_foreign_qualifier (staged
  error: w6c 'is not a variant of', w6c_ww rejects via the #95
  ambiguity guard — the carrier asserted reject polarity only)
929_match_4arm_cross_module_run.c -> r929_match_{4arm_shadowed_
  canonical,3arm_shadowed,5arm_shadowed,6arm_shadowed,4arm_mixed_kinds,
  4arm_shadowed_reverse} (run; single-file a+b packages, dropping the
  carrier's accidental doubled 'package b;' line)

929's byte-id upgrades from 994-delegated to per-fixture via
test-data-byteid; the asm-level distinct-CMPQ sentinel remains 728's
separate concern. All rows probed green on both stages.
2026-08-08 14:33:23 +09:00
c78bf6ebca test: port the xmod same-leaf collision carriers to corpus fixtures
752_modparam_callee.c -> r752_modparam_xmod_variadic_vs_scalar,
  r752_modparam_variadic_no_collision (run; alpha/beta module dirs)
784_xmod_alias_struct_collide_run.c -> r784_xmod_alias_struct_collide
  (run-exit 42), r784_xmod_alias_struct_symmetric (21),
  r784_xmod_alias_control (30) (sa/sb module dirs)
793_xmod_struct_argpush_collide_run.c -> r793_xmod_argpush_
  {recvpush_t16 107, fieldread_t16 107, combined_t16 114,
  recvpush_t12 6} (m1/m2 module dirs)
797_xmod_struct_field_layout_collide_run.c -> r797_xmod_layout_
  {ptrread_t16 107, letcopy_t16 107, addrptr_pq 10, addrval_pq 10,
  ptrwrite_t16 109, valwrite_t16 109} (m1/m2) + r797_nestfill_box (66)

r770-style sibling module dirs express the trees; the fixture corpus
runs both drivers natively (784's wwstage-build-only leg upgrades to
build+run) and test-data-byteid owns each carrier's aggregated per-
package .s compare (letcopy_t16's real discriminator). All 16 probed
green on both stages including per-package byte identity.
2026-08-08 14:33:23 +09:00
be852d51d9 test: port 732_def_const_fold residue to corpus fixtures
732_def_const_fold.c -> r732_def_xmod_fold (run-exit 42; stronger than
  the carrier's DATA-row grep — links AND reads the folded cross-module
  value), r732_def_xmod_cycle (error 'def value: reference chain too
  deep', upgrading the needle-less exit!=0 check).

The five same-module exec rows and both same-module fail-loud rows were
already owned by the r732_def_* fixtures; their cs-vs-ww byte identity
rides test-data-byteid, which now also covers the xmod fold row. The
timeout-124 hang distinction devolves to the wwfixture harness timeout.
2026-08-08 14:33:23 +09:00
f45360bbec test: port 696/697/699 resolver carriers to corpus fixtures
696_modtype_leaf_collision.c -> r696_modtype_leaf_pos (run-exit 26),
  r696_modtype_leaf_neg (staged error; w6c 'no field', w6c_ww
  asserttyped: dot — the old no-wwstage-checkfile comment is stale)
697_samemod_prefer.c -> r697_samemod_prefer (compile)
699_use_promote_alias.c -> r699_usepromote_{type,fn,def,var}
  (run-exit 42 each)

Single-file multi-package form folds the modcollision/samemodprefer/
usepromote data trees into the fixtures; the trees retire with their
carriers. The dir-tree .wwi transport of colliding type decls is owned
by test/byteid/wwi_test.ww. Coverage upgrades: 697 and the 699 rows
gain the wwstage leg the carriers gated or skipped, and all positive
rows enter the blanket test-data-byteid net.
2026-08-08 14:33:23 +09:00
50d70e74e4 w6c: append() accepts struct CALL rvalue elements (#34)
Evaluate the call pre-grow into a per-site scratch, receiving by the
N_LET matrix (sret / float / odd-tail / GP), then grow, slot, sized
ladder. Both stages, byte-identical. The two reject pins graduate to
16B GP accept rows; three new fixtures cover 24B GP, 3B/4B tails,
16B float, and 40B sret.
2026-08-08 14:10:55 +09:00
956246b3f0 test: migrate 700_e2e rows to corpus fixtures; retire the carrier
136 rows migrated, 0 already owned, 3 wwstage-failing rows held out.
2026-08-08 14:10:46 +09:00
74c9243ca3 test: retire 911_attest_record; record-continue owned by test/package 2026-08-08 13:47:16 +09:00
e1141330f2 test: retire 13 byteid carriers into the corpus blanket
Ten of the thirteen were already fully twinned — their runtime rows
migrated in earlier waves (r56_def_*, r79_*, r78_*, r926_*,
r989_m1union_*) and the carriers survived only for a cstage-vs-wwstage
byte-compare the blanket test-data-byteid comparator owns once the
source is a corpus fixture. The other three needed their remaining
inline sources added first: r756_* (4, alias-chain unwrap),
r844_bid_src (size(u64) untyped-int compare), r989_structframe_* (3,
struct-local frame layout). Every new fixture was verified both-stage:
compile exit 0 twice, .s byte-identical, runtime exit as pinned.

The five ww_ww-driver-leg carriers among them (785/786/789/790/
m1union) follow the 815/940/951 precedent: content identity owned by
their corpus twins, driver-leg identity owned by 989_lib_byteid's
sweep. Byte/artifact partition 24 -> 11; carriers 147 -> 134; corpus
pin 1495/2990; the stale 915 sweep figure in the docs corrected to
the true 1,157 non-error count.
2026-08-08 02:57:55 +09:00
67f39256f6 cgen: resolve chained-DOT fn-ptr field callees
fnptrcalleetfn's N_DOT arm required an N_IDENT base, so a.b.cb(...)
fell to the name registry with an empty module hint and emitted
CALL cb(SB) (undefined symbol; cstage calls the stamped ptr indirect).
dotbasestructinfo resolves the base chain through the struct registry
— each link a struct- or *struct-typed field — and the single-dot
path routes through the same resolver unchanged. Closes the last open
shape of the #59.8 name-keyed callee family. Fixture fnptrfield_chain;
corpus pin 1487/2974.
2026-08-08 02:48:05 +09:00
430c7e0546 check: compare variadic fn params at the declared element type
installparams normalizes a decl's `T...` param lhs to []T in place
(cstage instead sets the resolved tp->type and never mutates AST), so
typeeqast compared a decl-synthesized fn type's []i64 against a fn
TYPE expr's surface i64 and confidently rejected
`let f: fn(args: i64...) void = sum;` — cstage accepts and runs. The
normalization wrapper now carries an op marker and typeeqast peels
exactly it, so both sides compare at the declared element type without
admitting genuinely different element depths. Fixture fnptrlet_variadic;
corpus pin 1486/2972.
2026-08-08 02:47:08 +09:00
5abb1e6069 wwstage: port struct embedding; graduate #59.13
The last frontend-gap pin: wwstage had no Hare struct embedding
(struct { hash.hash, ... }), rejecting lib/crypto/sha256 at parse.

- parse.ww: the three member forms (named / anonymous struct / bare
  dotted-ident embed), consume-then-branch since this parser has no
  peek; embeds carry f.str == "" and the type in f.lhs.
- check.ww N_TSTRUCT flatten: promote the inner struct's flattened
  fields at base+src.offset (check.c:961-990); the embed is one
  nested-struct unit in the slot ladder; the resolved inner AST is
  planted on the TFIELD rhs for cgen.
- check.ww walkers: astoffset / exprtype N_DOT / #251 struct-lit
  field lookups descend embeds through shared helpers; the collision
  and non-struct-embed rejects live in validatestructfields (the
  once-per-decl diagnostic site).
- cgenutil.ww registerstruct: regfieldrun walks the AST against the
  flattened tfield cursor, descending embeds via the planted inner
  AST so promoted fieldinfo entries keep the inner field's own name
  and type node.
- wwi printers unchanged (both stages already emit nameless fields).

sha256_test compiles byte-identically end to end and its 6 tests
pass; 989_lib_byteid is now 44 id / 0 divergent / 0 wwreject.
Fixtures r5913_* (promoted rw, offset shift, anonymous embed,
two-level embed + promoted fn-ptr callee, three rejects); corpus pin
1485/2970.
2026-08-08 02:37:50 +09:00
6553d60e91 cgen: peel *fn field types for local struct-field callees
fnptrcalleetfn's N_DOT arm accepted only a bare N_TFN field tnode, so
a call through a field declared `cb: *fn(...)` missed, fell through to
the name registry, and emitted CALL cb(SB) — an undefined symbol
(loud at link; cstage calls the stamped ptr-to-fn indirectly). Same
TPTR peel the N_IDENT arm already had. Fixture fnptrfield_call covers
the by-value and via-pointer shapes; corpus pin 1478/2956.
2026-08-08 02:28:33 +09:00
f6628b985e test: migrate 24 residual carriers into the fixture corpus
Audit-driven migrate-and-retire wave (one read-only auditor per
carrier batch, verdicts row-checked through the full corpus gate):
every retired carrier's assertions are now owned by declarative
fixtures or an existing owner. 238 new fixtures land (corpus 1,239 ->
1,477; cells 2,954), covering enum/str/slice/tagged array elements,
inferred-length arrays, global slice/str zero-init and literals,
global array/pointer field reads and stores, struct-literal slice
fields, struct returns, float arithmetic, size/int limits, sort and
log-vstream behavior, place-addressed stores, and append places.
Rejection rows STRENGTHEN the old nonzero-exit checks to required
diagnostic fragments; all byte-id claims fold into the blanket
test-data-byteid sweep (now 1,142 compared, 0 pinned-divergent).
Four carriers were fully redundant with existing fixtures/lang tests
(749, 722, 765, and 771's byteid rows) and retire without new rows.
Native carriers 171 -> 147 (residual 135 -> 111).
2026-08-08 01:29:15 +09:00
3f7452814b cgen: resolve local fn-ptr callees for variadic arg prep
The wwstage variadic call classification was name-keyed: a fn-ptr
FIELD call whose local base shadowed the current module name
(lib/log's log.println(log, args...)) picked the module fn's
signature — nfixed off by one, the fixed arg boxed into the gather,
the spread emitted as zeros — and a no-collision fn-ptr callee missed
the registry entirely, leaking the raw N_SPREAD as a single $0 word
(SIGSEGV / exit 255 in 8 of 11 logtest tests on the wwstage leg).
fnptrcalleetfn resolves a local fn-ptr callee (bare local or struct
field) to its N_TFN once, shared by the CALL-target choice,
callee_variadic_param (with the []T wrap registry params get from
installparams), calleecvariadic, and the widening param lookup, so
target and arg prep can never disagree. Graduates the #59.8 logtest
pin — DATABYTEID_DIVERGED-era M_DIVERGE count is now zero.
2026-08-08 01:07:23 +09:00
c18005d833 check: stamp SK_TYPE value idents with the per-decl NAMED
A TYPE name used as a VALUE (an error-singleton `return too_long;`)
stamped the flattened BODY type, so structurally identical !void
singletons in one union were indistinguishable and flatvariantidxt
loud-rejected the ambiguity — the real cause of the lib/path wwstage
reject (the pinned #120/#29 global-slice-const blame was stale; that
family had drained). The N_IDENT arm resolves through a synthesized
TNAME (the #66 N_STRUCTLIT precedent); the module-qualified N_DOT twin
reads the sym's cached NAMED. Graduates the path M_WWREJECT pin (#142).
2026-08-08 01:00:03 +09:00
125f626697 check: type the N_BIN operand of as in the wwstage
An enum OR-fold under `as` ((m.A|m.B) as u32) was never typed:
scruttype resolves IDENT/DOT only, and the post-order restamp then
re-derived the folded member literals as untyped_int. The unstamped
operand missed cgtypeassert's #27b enum-reinterpret gate and lowered
as a phantom tagged assert — unconditional exit 1 at runtime on the
wwstage leg. checkisas now types an N_BIN lhs and the N_BIN restamp
preserves an existing enum stamp. Graduates the #59.9 stattest pin.
2026-08-08 00:59:02 +09:00
c421c2b20a cgen: key the str/slice arg recognizers off the checker stamp
The wwstage nodeisstr/nodeisslice recognizers were name-keyed for
every non-local shape: an indirect fn-pointer callee ((*f)() — the
errnotest #59.5 divergence, streq receiving a shifted register file)
and module-global let/const idents (path.sepstr — union-widen pushes
zero-filled len/cap) both fell to false while cstage keys off the
checker stamp unconditionally. Both recognizers now fall back to the
stamped n.type_; push and pop sites share them, so the drain stays
balanced by construction. Graduates the #59.5 errnotest pin.
2026-08-08 00:58:04 +09:00
0b23f9fb31 test: fold driver-parity byteid into 989_lib_byteid, retire 815/940/951
Wwstage-driver-leg byte identity gets one owner. The three carriers'
content identity is already blanket-owned by their r815_/r940_/r951_
corpus twins via test-data-byteid; their only non-redundant assertion
was the ww_ww driver leg, which 989_lib_byteid's 43-unit sweep covers
apart from the zero-dep root-only edge — folded in as a new probe row
(44 units). r940 fixture comments re-cited off the retired carrier.
2026-08-08 00:36:11 +09:00
66251cc52b cgen: default the hi bound of a slice/str-field slice, both stages
x.slicefield[:] / x.strfield[2:] emitted MOVQ $0 for the omitted hi
bound on BOTH stages (N_IDENT-gated dispatch; symmetric, so byte
identity never caught it) in all four sites: cgexpr N_SLICE + pushargs
(cstage), cgslice + pushargsrev (wwstage). The new arm re-evaluates
the pure field read for its {ptr,len,cap} header and takes .len,
covering local, viaptr, dot-chain, (*p), arr[i], and global inners.
Call inners still loud-reject upstream. Sibling of the #252/#257
array-field arms.
2026-08-08 00:29:51 +09:00
cc22abfc04 cgen: load the full header for a wwstage global-dot slice field
let x: []T = g.buf on a module-global struct dispatched only str
fields to the 3-word header load; a []T field fell to the scalar
tail (ptr word only), so len/cap read stale registers and the shape
was byteid-divergent against cstage's TY_STR||TY_SLICE arm (#263).
2026-08-08 00:23:55 +09:00
8a9be47f65 test/wcc/data: pin the drained divergences at runtime
The four graduated pin families byte-compared under test-data-byteid
but their original fixtures masked the miscompiles at runtime (lone
frame slot, dead code path, pointer accident). Add the discriminating
programs as run fixtures: adjacent-element clobber for the 2-byte
deref store, second-variant match dispatch for fn-type dedup, payload
integrity across the tagged widen of a padded struct, and base64
clear() actually zeroing its buffers. Corpus pins move to 1,229
fixtures / 137 run / 766 run-exit / 2,458 cells with the new identity
hash; the data-byteid floor moves to 915.
2026-08-07 23:55:01 +09:00
83c8a4f34f test/wcc: retire 990_selfhost; its live assertions move to their owners
Every probe's assertion is owned by a current gate: the compile and
link probes by make all and the bootstrap fixed point; build/run and
cs/ww byte identity by the fixture corpus, test-data-byteid, and
989_lib_byteid; wwstage driver and toolchain parity by 993/995;
checker-diagnostic parity by the corpus' both-stage //ww:error rows.
The wwdump -t/-a dump-parity probes gated the frontend port's
convergence, which the compiler-output identity gates now own end to
end; carrier ran green at retirement.

What was still uniquely alive migrates: smoke.ww becomes corpus
fixture selfhost_smoke (upgraded from a cstage-only build to both
frontends, byte-identical, exit 42 on both toolchains; corpus pins
move to 1,225/763/2,450 with the new identity hash in the same
commit), and sym_link.ww's scope/sym behavior rows become in-language
lib/ww/syntax/symtest.ww under LIBRARY_TESTS. uses.ww (parser-stub-era
-a fixture) and the already-orphaned tagged_ptr_ret.ww/trypromote.ww
retire with the probe corpus. Bootstrap native gates drop to six;
frontend numeric-sync comments now cite the rule-6 mirror instead of
the retired diff probe.
2026-08-07 23:34:44 +09:00
d456263f7e test/wcc/data: grow the declarative compiler corpus to 1,224 fixtures
The r-prefixed waves absorb the runtime, reject, and byte-compare rows
of the migrated native carriers; each fixture is one directory with one
case.ww and a //ww:error, //ww:compile, //ww:run, or //ww:run-exit
directive covering both frontends.
2026-08-07 23:21:04 +09:00