Flip the soft-default to a hard "missing package clause" error symmetrically in
both stages (cmd/wcc/parse.c + lib/ww/syntax/parse.ww): the first real decl of a
primary section with empty pathmod/resetmod and no seen clause is now rejected.
Closes the documented soft-default divergence (the 63-wrapper carve-out).
The gate flip can't be split from the migration it breaks, so this is one atomic
commit: ~80 test/wcc wrappers gain `package main;` via a shared wwtestpkg.h
helper, 6 data fixtures plus 17 asm-grep assertions update for the bare->main.<leaf>
root-helper mangle shift, and rt/ declares `package rt;` with @symbol pinning the
bare rt_ensure/rt_malloc linker names.
Root mangling narrows: the executable entry `main` stays bare (existing
carve-out), but root helper symbols become main.X. The #84 cluster is rewritten
to assert main.run distinct from aa.run/test.run; its cgen fix and bare machinery
are retained — still load-bearing for package-less module-reset deps. New
table-driven test 782_strict_package.c (6 rows, both stages).
Retiring //ww:module-reset is deferred to #24b: it is load-bearing (clears the
.wwi pathmod so the body's package clause asserts), not a vestige; fusing its
removal here would be a silent mismatch.
All byte-id gates green; full make test reports "all 335 tests passed".
A ww `str` becomes a 24-byte {ptr,len,cap} value, identical in layout to
[]u8 -- the enabling prerequisite for the Phase 2 `str == []u8` collapse.
Both stages, atomically:
- ty_str 16->24B; str value flows 3-reg AX/BX/CX (was 2-reg); str literals
emit cap (=len).
- str in a tagged union grows to a 32B slot, using the AX/DX/CX/R8 4th-word
path already used by 32B slice-variant unions -- str-variant is now
structurally identical.
- tuple (scalar,str) return: 4-reg AX/DX/CX/R8 + 32B receive, extending the
existing type-keyed return (no sret).
- str == []u8 for index and .ptr/.len/.cap, kind-gated where size-based
dispatch collided at 24B; cstage and wwstage mirror exactly.
- table-driven runtime coverage: test/wcc/928_str_abi_run.c.
Cannot be split (rule 10/11): a 24B str and a 16B str cannot coexist across
the two compiler stages without breaking byte-identity, so the size change
and every dependent ABI/codegen site land in one atomic commit, both stages.
Known follow-ups (zero corpus impact, tracked): str-literal global .cap
static-init; >16B struct by-value (pre-existing); tagged-union
match-scrutinee stage divergence (pre-existing).
Wwstage's N_INDEX-lhs cgassign dispatch chain had a triple-site
N_DOT base gap (sister latents filed during #24 / #27 review):
Read (#28): `obj.mat[i][k]` over a struct field mat: **u8.
cgindex routes the outer N_INDEX's N_INDEX base through
indexvaluetnode; the recursion bottomed out at the inner
N_INDEX's N_DOT base with bt=nil. esz fell through to 8 +
signed_elem to false — wwstage emitted a stray outer
`MOVQ $8, CX; IMULQ CX, AX` plus `MOVQ (AX), AX` (8-byte
read over a 1-byte u8) instead of cstage's bare
`MOVZBQ (AX), AX`.
Write (#30): `obj.arr[i] = v` over a struct field arr:
[N]Tagged (e.g. (i64|str)). cgassign's N_DOT-base arm
computed esz via indexbaseesz but never set elemtn, so the
tagged-element store gate missed and the 24-byte tagged slot
was overwritten by a single scalar MOVQ — wrong-width store
+ tag/payload junk in the upper 16 bytes.
Cstage walks `n->lhs->type` directly via the typed AST
(cmd/w6c/cgen.c idx_eff + the N_INDEX-lhs N_ASSIGN branch).
Wwstage now mirrors via indexvaluetnode, which #24 (aa8ca47)
introduced for the N_INDEX-base case; #28/#30 graduate it for
N_DOT base via the existing dotfieldtnode helper.
Bundle graduates N_DOT base for the entire N_INDEX-lhs cgassign
chain: (a) indexvaluetnode in cgenutil.ww handles N_DOT base via
dotfieldtnode; (b) cgassign N_DOT-base arm in cgenexpr.ww calls
indexvaluetnode for elemtn; (c) scanlocals N_DOT-base arm in
cgendecl.ww parallels the existing N_IDENT arm for tagscr-bump.
Splits are bisect-incoherent: (b)-alone clobbers locals via
under-sized frame, (a)-alone leaves the write path with wrong
elemtn, (c)-alone has no consumer. Only the triple delivers a
complete N_DOT-base graduation matching #24's N_INDEX-base
pattern.
Cstage's first-use+fail-loud strategy for @tagscr (#26 commit
069548d) handles the N_DOT-base shape naturally; the scanlocals
N_DOT arm is wwstage-specific. Long-term rule-10 convergence
(wwstage DOWN from scanlocals to first-use+fail-loud on BOTH
stages) is filed as task #15.
Class A wwstage cgen UNDER. No in-tree consumer; sister latents
filed during #24 + #27 reviews. Test 741_dotbase_chained pins
the dispatch + cstage-byte-identical asm for both rows.
Sister latent (filed): indexbaseesz has no N_TARRAY arm for
scalar struct-field array writes — `s.arr: [N]i32` scalar write
falls through to esz=8 on wwstage. No in-tree exerciser; tight
scope kept here.
115/115 ok. ww2 == ww3 == ww4 byte-id.