16c83e70d34194c9eb537757d9bb4e98c770da18
622 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| f8be2ae8dd |
wcc/cgen: #116 non-literal tuple source into a tagged box (both-stage)
cg_widen_tagged_store only handled a tuple LITERAL (N_TUPLE / cast-of-
N_TUPLE) widened into a tagged box; any addressable non-literal tuple
source -- IDENT var, INDEX tbl[i], DEREF *p -- hit the `else fatal`
("tuple-typed source shape unwired"). Both stages loud-identical
(honest, no silent miscompile). This blocked indexing a const tuple
table into a union (regex charclass_map[i] -> charset union).
Add an addressable-tuple-source arm, both stages (cgen.c +
cgenutil.ww twin). It resolves the source address via the cgplaceaddr
place-spine (covering ident/index/deref -- one mechanism, so the trio
is family-closed) and block-copies the tuple's type-table ->size bytes
into the box payload (after the 8B tag), then stamps the variant tag.
No re-slotting: a tuple's in-memory layout uses the same eslot strides
(str=24B header, *fn=8B, ...) as the box payload the literal loop
fills, so source-layout == dest-layout. The existing narrow-pack and
tag-unresolved guards stay as the honest boundary; CALL/sret tuple
sources (different receive, #68-kin) stay loud.
align-BOTH: both stages were loud (no runtime reference), and byte-id
is structurally blind to an identical-wrong emission -- so correctness
is proven by a RUNTIME read-back pin (944_nonlit_tuple_widen_run, per
shape: match-extract + assert str header + call the fn-ptr elem with
distinct fns so a stale pointer is caught). 936's old reject row
graduates to a run row. Both stages byte-identical (990-997 green).
|
|||
| da30f10f70 |
wcc_ww/check: #47 gap-B tuple-with-tagged case-arm variant-match (align-up)
wwstage's checker rejected a `case let t: ((void|size),(void|size),
size) =>` arm against a (tuple|error) scrutinee ("case: not a variant
of scrutinee"), while cstage accepts and runs it. typeeqast's N_TTUPLE
arm recurses per-element, but a tagged element (void|size) is
N_TTAGGED -> fell to the conservative catch-all `return false`, so the
whole tuple-compare failed. typeeqast is the sole acceptance route
(casevariantpairmatch is N_TNAME-only).
Add an N_TTAGGED arm to typeeqast, sibling of N_TTUPLE, mirroring
cstage type.c:288-300 (type_eq TY_TAGGED): position-by-position
variant compare over the tagged node's .list (direct nodes, not
.lhs-wrapped). cstage's nullable-flag check is deliberately not ported
(resolved-Type property, no ww AST analogue; moot for case-match).
A spread variant (TK_ELLIPSIS) in the .list is loud-rejected rather
than compared: a naive streq would silently accept a `...ab` case that
cstage rejects (a new cs!=ww over-accept the bare arm introduced).
Flattening the spread is deferred (#115); until then it louds, matching
cstage.
ww-only (cstage already accepts); the gap-A cgen store landed in
|
|||
| 6a5bb3efc9 |
wcc/cgen: #47 gap-A tuple-in-union tagged-element store (both-stage)
A tuple containing a tagged-union element, used as a union member (e.g. ((void|size),(void|size),size) | error), loud-stopped in the cgen return-store: the tuple-in-union store walk had scalar/float/ str/slice element arms but no TY_TAGGED-element arm. A PLAIN tuple-in-union already worked -- the blocker was the tagged element. Add the recursive two-level widen arm at both stages (cg_widen_tagged_store / cgwidentaggedstorebp): for each tagged element, re-enter the tagged-box store (inner tag@slot+0, payload@slot+8) at the element's tuple-payload offset, then stamp the outer tuple tag. Slot strides come from the type table (roundup8(eu->size)) -- the checker already sizes the shape correctly (tuple->size measured 40, union box 48; check.c:715-720). The recursion descends a finite type tree (a tagged element is never a tuple literal, so it can't re-enter the tuple arm); unsupported deeper nesting still louds via the existing size/tag guards. Both stages get the same arm -> byte-id (990-997 green; additive, bootstrap-neutral). cstage runs the full b1c shape (construct+return+match-extract) as the runtime reference; wwstage's store rides on byte-id until gap-B. gap-B (wwstage checker match-acceptance of the tuple-with-tagged case pattern) is a separate commit -- wwstage still louds the match honestly at the checker. Pin 944_tuple_tagged_union_run. |
|||
| 351abb0ab3 |
wcc/cgen: #58 indexed tagged-field read+assign cursor arm (both-stage)
Reading or writing a tagged field of an indexed array element (xs[i].field) was broken on BOTH stages, byte-identically and silently (#263 gate-blind): the arr[i].field branches had arms for array/str/slice/float but no TY_TAGGED arm, so the tagged field fell to the single-word scalar path. READ loaded only the tag word (stale payload -> `xs[i].min as T` read garbage); ASSIGN stored the raw unboxed scalar into the tag slot, corrupting the box. Insert a TY_TAGGED cursor arm before each scalar fallback, both sites both stages (cgen.c read + assign; cgenexpr.ww cgdot N_INDEX-lhs read + cgassign indexed-field). READ mirrors cg_tagged_memread (payload -> DX/CX/R8, tag -> AX last). ASSIGN synthesizes the tag for the concrete variant (taggedvariantindext) and stores tag+payload via the str/slice 3-word store spine -- not the source-remap widener (concrete rhs has no source tag to remap). >32B / multi-word / float payloads are loud-stopped at all four arms (emission not yet wired; see #114). That shape is reachable today via a narrow-variant ctor, so it louds rather than silently miscompiling. Both stages get the same arm -> byte-id preserved (990-997 green; the runtime is the net for this #263 class). Pin 944_idx_tagged_field_run (read/assign runtime rows + >32B expect-loud rows). |
|||
| cc896bd078 |
wcc_ww/cgen: #55 tagged-source arg-widen into wider tagged slot (align-up)
wwstage pushargsrev treated a narrower tagged-union argument widened into
a wider tagged param slot as a concrete variant: taggedvariantindex<0
clamped the tag to 0 and pushed word0 only (deref/index/dot silent-wrong;
ident ran correct only by prefix-union tag-index luck). cstage is correct
(cg_widen_tagged_push routes src_is_tagged unconditionally); align ww UP.
Three arms in cgenutil.ww, all mirroring cstage cgen.c:
- slot-gate the ident aistagged short-circuit so a slot-differ tagged
ident falls to the widen path instead of the raw 2-word push;
- route a tagged source in the widensz>0 arm through @tagscr +
cgwidentaggedstore + push high->low (cgen.c cg_widen_tagged_push);
- cgwidentaggedstore cursor arm (<=32B INDEX/DOT) spills by source
width, zero-pads, and tag-remaps (cgen.c 2698-2714) — was dst-slot
spill of stale high regs with no pad and no remap.
Same-slot tagged->tagged is byte-id-neutral by construction (empty pad +
identity remap). cstage untouched; 4 legs x {aligned, misaligned-tag}
converge ww->cs byte-identical. Pin 944_tagged_widen_arg_run.
|
|||
| 00d9580c9f |
wcc/cgen: #84 uninit [N]T array zero-fill (both-stage)
Drop the `!TY_ARRAY` exclusion in the bare-let no-rhs zero-fill (cgen.c N_LET else + cgenstmt.ww cglet, both gated `sz>8 && !TY_ARRAY`) so an uninit `[N]T` array local zero-fills like every other composite (Go-zero per user ruling). The zero-fill extent is the array's chased ABI size (lu->size / chased tinfo.size, rule-13 — never a hardcoded count*esz), NOT the slot-padded letslotsize, so a non-8-multiple array ([20]u8 = 20) zeroes its exact bytes instead of over-zeroing to the 24B slot. The unrolled MOVQ/MOVL/MOVB run mirrors the existing composite path; the largest real local array ([256]u8) is 32 MOVQs (pathbuf[4096] is a module GLOBAL, BSS-filled — never on this stack path, so no large-fill case exists). Closes a gate-blind #263-class bug: `let a: [3]int;` (no init) read whatever the stack held — a clean frame masked it (fresh stack = 0), a dirtied frame exposed it (d_array=165 garbage). BOTH stages emitted no fill, both-wrong-IDENTICAL, so the cs==ww byte-id net could not see it. The load-bearing net is therefore a RUNTIME dirtied-stack zero-read (944_array_zeroinit_run: array-elem / narrow [4]u32 / non-8-mult [20]u8 / 2D + an initialized control), not asm presence. Deliberate byte-id EVENT: every uninit-array source site gains zero-fill insns, so the 990-997 .s MOVE vs the prior tree; cs==ww HOLDS (both add the identical insns). The 990-997 byte-id + 995 self-rebuild staying GREEN is the fixpoint proof — it proves every uninit compiler-array is write-before-read, so the zero-fill is purely additive and the ww1->ww2->ww3 self-rebuild fixpoint holds by construction. w6c/wwdump main.combined.ww regenerated (cgenstmt.ww embeds there). #84 is ARRAY-ONLY; the no-default reject-set (uninit tagged / plain-*T) is split to #113, parked behind a ruling — selfhost relies on the current (void|T) zero-fill (the "not-set-yet" idiom). |
|||
| 5d596206c6 |
wcc/cgen: #94 def-array indexed &-base leg (both-stage)
`&D[i]` over a module-level DEF array SEGV'd on BOTH stages: the TK_AMP N_INDEX N_IDENT base classify checked only the local and let legs, so a def-array base fell to a wrong else — cstage zero-based the addend (XORQ BX,BX -> wild pointer, cgen.c) while wwstage value-loaded the symbol (MOVQ name(SB) = D[0], not its address, cgenexpr.ww complex-base fallback). Divergent asm, both wild. Add one def-array leg per stage, mirroring the working let leg: - cs: `def_isarraydef(base) -> LEAQ name(SB),BX` alongside let_islet. - ww: the `defvartnode` fallback the read-side cgindex already takes (cgenexpr.ww:1762) -> N_TARRAY classifies isglobalarr -> LEAQ name(SB). The def DATA symbol already exists (plain &D + D[i]-read work), so once the base is the address the existing i*esz scale + ADDQ round-trips. cs and ww now emit BYTE-IDENTICAL LEAQ-SB asm — the both-broken -> both-correct convergence is the point (#263 class). Rows (944_def_amp_idx_run, all 0/0 byte-id): amp_int [3]int, amp_u32 [3]u32 esz=4 (narrow scale), amp_arg &D[2] as a func-arg; controls ctrl_plain (&D), ctrl_read (D[i]), ctrl_2d (&M[1][1]) keep working. *p spelled `let v: T = *p` — `*p: T` parses as `*(p: T)`. OUT (filed #112): &D[..] slicing a def-array is a distinct parse reject needing a Hare-fidelity ruling — not this leg. |
|||
| 26ba1ad1b5 |
wcc_ww/cgen+peellint: #109 close #101 primsize-alias family by construction
Route the 16 routable bare-primsize GUARD sites (is-primitive / struct-vs-prim dispatch) through the #101 aliasprimsize SSoT helper. Byte-NEUTRAL by construction: an alias-narrow name is already neutralized downstream by the same arm, so routing emits no new asm (the empty-flip-set ken oracled). Shape-A exclude-prim-early (3): cgenutil sretretsize / structparamsize / structfloatclass — `primsize>0 return` then structlookup→nil returns the same value; route returns it early, same. Shape-B prim-guard-then-structlookup (13): cgenutil 4604/4650 + cgenexpr 4136/10244 + the 9-site CALL/assign cluster — primsize==0 →structlookup→nil→fall to normal; route skips the block→same normal. Install the peellint bare-primsize FINALE (B7 lint-fuse contract): tools/peellint now rejects any bare primsize() in the ww stage outside the annotated whitelist. Evasion-hardened per the B7 lesson — a character scan (comments + string/char literals stripped first) and a LEFT+RIGHT word-bounded match of the bare `primsize` TOKEN (not just `primsize(`), so the aliasprimsize() wrapper is never a hit and every compiling spelling reds: the call primsize(nm), the paren-wrap (primsize)(nm), the function-value bind `let p = primsize`, and any line-split. ww-only (the C stage dealiases via type_chase_named, no primsize symbol). Two independent exemption windows (peel-ok vs primsize-ok) so neither rule blinds the other. Runs as a make-test dep. Whitelist the 6 designed exemptions with primsize-ok WHY-annotations: machinery — aliasprimsize body (SSoT chase) | typenodeprimresolved + exprprimresolved (#11/#33 prim-resolver chasers) | cgcast leaf-loop + cgenexpr #11 deref-store (own ps==0 fallback; route would regress #11) | the primsize oracle/definition itself (nothing below to chase). structural — elemsizeof x2 + paramfieldsize (chase lives in the -c twin elemsizeofc; threading c is the dormant #110). Empty-flip-set proof: zero C bytes; cstage binaries bit-identical; bootstrap byte-id 990-997 + 950 all green (w6c == w6c_ww on the full selfhost, self-rebuild identical); combined.ww (w6c + wwdump) regen idempotent; sizelint 0; peellint 0 (raw-peel AND bare-primsize over the whole tree = the close-by-construction proof, zero unwhitelisted survivors). Tests: 944_peellint_gate +14 rows (bare / space-before-paren / name-at-EOL split / string-blind opener / paren-wrap / fn-value-bind RED; aliasprimsize wrapper + primsize-ok annotated GREEN; corrupt annotation RED; independent peel/primsize windows; C-file out-of-scope). Closes the #101 primsize-alias family by construction. #109. |
|||
| 4459a49d3a |
wcc/cgen: #87 plain tagged-union module-global DATA + match SB-resolution (both-stage)
A PLAIN (non-alias) module-level tagged-union global SEGV'd on BOTH
stages: no static DATA was emitted (let_emit_size/letemitsize returned 0
for TY_TAGGED) so the global was never registered, and the match
scrutinee resolved it as a frame-local at offset 0 — reading saved BP as
the tag. Two sub-sites, one route (neither half ships alone — DATA
without SB-resolution still SEGVs; SB-resolution without DATA reads
nothing):
(a) DATA-emitter — a non-nullable TY_TAGGED arm emits the box that
byte-MIRRORS a runtime LOCAL of the same type: tag word at +0 (the
const-selected variant index via cg_tag_for_variant / taggedvariant-
index), payload at +8, zero-padded to the union box size. int and
str/slice literal variants are wired (str carries a DATAR ptr patch
at +8); any other variant payload loud-stops (rule 7). emit_tagged_
data + emittaggeddata are the per-stage twins; let_pre_intern/
letpreintern gain the matching str-variant intern. Nullable stays 0
so the (*T|void) one-word fold keeps the 8B scalar arm.
(b) match-scrutinee global resolution — the PLAIN-tagged twin of #78:
a global tagged ident scrutinee LEAQs name(SB) and copies the box
into an @match_spill slot the dispatch indexes off BP.
DATA target (mirror of the local box, verified byte-for-byte): for
(i32|str)=42 the 32B box is tag0 | 42@8 | zero-pad; for ="x" it is
tag1 | ptr0@8(DATAR _S_n) | len@16 | cap@24. cs and ww emit byte-
identical asm.
Pins (rob §3, dual-stage 910 cstage + 997 wwstage, attest_pass.ww): the
tagged global-vs-local byte-identity pin (match over the GLOBAL gives the
same arm/value as over a LOCAL — was SEGV both stages) and the str-
variant tag-1 pin, plus the #86 tuple global-vs-local lock-pin guarding
the already-correct emitter path.
929 fail_global_src graduates: a >48B tagged GLOBAL by-value arg now
resolves through the cgplaceaddr MEMORY-class arm (LEAQ g(SB) + blit)
instead of the #38b loud-stop, and runs correctly (uninit zero box ->
first variant); the row becomes a positive run pin. The struct-variant
>48B init still loud-stops via the data emitter.
The first-class-VALUE copy of a tagged ident (`let q = g`) stays a
pre-existing silent #49/#46 sibling (local and global identically),
filed separately — out of this fold's two sub-sites.
|
|||
| 3546673756 |
wcc_ww/cgen: #63 alias-named struct-lit fill via structlookupchain (let-init + sret-return)
cglet's N_STRUCTLIT init arm resolved the struct by a bare
structlookup(c, sname). For an alias-NAMED literal
(`type rep2 = rep; let r = rep2{id=6}`) the type ref carries the
alias name "rep2" but only the base `rep` is registered, so the
lookup returned nil and the field-fill never fired. The nil then
split by slot size into two symptoms of one root:
- <=8B: the small-let scalar default zeroed the slot and DROPPED
the literal (SILENT wrong — the field read 0), and
- >8B: no fill arm matched, falling to the cglet "unhandled rhs
shape" LOUD (task #7/rule-7).
Route the arm through structlookupchain (the #92/W2 SSoT already
adopted at cgenstmt:1974/:2687), which chases the alias chain to the
base struct. trefn (rhs.lhs) is already the N_IDENT/N_TNAME type ref
structlookupchain accepts, so the bare sname extraction is dropped.
cstage operates on the resolved Type* via type_chase_named and was
always correct: ww-only align-UP, cs UNTOUCHED.
ROUTED (the two reachable silent sites, one class):
:2421 local N_STRUCTLIT let-init — the #63 repro hits it for
both the <=8B silent-zero and the >24B loud symptoms.
:1250 >24B sret RETURN twin (reviewer-63). sretretsize chases
the alias for the size GATE so this sret arm fires, but
the fill used the same bare structlookup(sname) — for an
alias-named >24B literal it returned nil and the fill was
SKIPPED, so the callee returned an uninitialised sret
buffer (SILENT wrong, runtime-0; cs correct). Same root,
same symptom, sibling site → folded by construction.
DECLINED (traced, not blind-routed; rule-11 + the #101 precedent):
:2625 N_IDENT struct-copy — also bare-structlookup but the copy
falls through to a generic path byte-identical with cstage;
both stages run correct. The post-copy field-READ diverges
(cs direct-offset vs ww LEAQ-indirect) = the #81/#65 alias
field-read class, out of #63 scope.
:2511 N_CALL struct-recv — blocked UPSTREAM by the aggregate-
return shape (#272/#277); ww louds at the sender.
:1363 <=24B register RETURN — alias case louds via the same
scalar-default catch (#277), not silently wrong.
The 2 already-chasing sites (1974/2687) untouched.
CONVERGENCE: m3_letinit_typed + m3_letinit_untyped (ww silent-zero ->
6/6 byte-id) + m6_letlit_alias (ww loud -> 7 byte-id) + sret_return_-
alias32 (ww silent-0 -> 10 byte-id), plus a non-alias control
(no-regress). Bootstrap byte-id NEUTRAL (selfhost has no
alias-struct-litinit/return; all 4 selfhost tools cs==ww confirmed).
Test: 944_alias_structlit_init_run (5 rows x cs-run + ww-run +
cs==ww byte-id = 15 checks), Makefile-wired.
|
|||
| 45f5415209 |
wcc_ww/cgen: #101 narrow-alias fill-stride via aliasprimsize SSoT
A struct-literal array fill sized a narrow-alias element off a bare
primsize(name): `type my32 = u32` gave primsize("my32")=0, so the
element width defaulted to 8 and a [3]my32 strode MOVQ -24/-16/-8 —
field n collided with arr[2] (kw1_101 run exit 1). cstage chases
my32->u32->4 (MOVL stride-4) at the twin sites and is runtime-correct;
this is a ww-only align-up, cs untouched.
Fix: a new aliasprimsize(c, nm) SSoT helper — primsize(nm), else an
aliaslookup-chase N_TNAME loop then primsize — and route the SIZE-use
primsize() family through it. The 7 c-bearing bare-no-chase size-use
sites are routed: cgen:993 (letemitsize), cgenstmt:1896 (cgarrlitfillbp),
cgenutil:1579 (elemsizeofc fallback)/1657+1665 (nodeprimwidth)/4791
(cgstructlitfill = the kw1_101 site), cgenexpr:6902 (cgcall vararg esz).
This is the rule-13 close-by-construction shape (one accessor for
"resolved primitive size"), not a per-site patch.
kw1_101 is the SOLE asm mover (byte-id NO->YES, run 1->0, MOVL
stride-4); every other routed site is latent/byte-neutral. Bootstrap:
all 5 combined units stay w6c==w6c_ww byte-identical. sizelint 0,
peellint 0, test-unit 296/296.
Scope fence (rob route-7-decline-6 ruling): three DESIGNED-exemption
sites carry inline primsize-ok annotations — elemsizeof :1475/:1499 and
paramfieldsize :3541 are structural (no-`c`, non-chasing) BY DESIGN;
their alias-chasing twin elemsizeofc is the routed :1579 leg. These are
the #109 peellint-whitelist seeds. Three further declines are already
correct chasing paths, not bare-no-chase bug shapes (typenodeprimresolved
:2026 / exprprimresolved :2063 are the chase machinery itself; cgassign
:7631 already chases via typenodeprimresolved, #11). The ~17 GUARD sites
(is-primitive dispatch) + the peellint finale are the committed #109
follow-on. Threading `c` into the structural sizers is dormant #110.
#101
|
|||
| c9cfa52624 |
wcc/check: #103/#108 inferred untyped-int defaults to int (8B), both stages
cstage type_default(TY_UNTYPED_INT) returned ty_i32 (4B): an unannotated `let x = <v>` / `let a = [<v>,..]` silently TRUNCATED any value > 2^31 (5000000000 -> 705032704) and strode inferred arrays at 4. wwstage kept the element raw untyped_int (size 0), which sized INCONSISTENTLY across cgen — the array STORE strode the 8 sentinel but letslotsize under- allocated the frame (SEGV) and cgindex strode the READ at 1. The two stages were each wrong differently; #263 polarity: cstage was the truncating side. int = machine word = 8B (Go-style, MEMORY project_int_machine_word_derived_limits); Hare lowers a flexible iconst to `int`, never a fixed i32 (ref/harec/src/types.c:835). Fix, one root, both stages (FUSE — the cs default + the ww concrete element must land together, else the inferred array is transient cs!=ww): - cmd/wcc/type.c type_default(TY_UNTYPED_INT) ty_i32 -> ty_int. The root; stops scalar AND array truncation at source. - cmd/wcc/check.c N_ARRLIT empty-elt fallback ty_i32 -> ty_int. Symmetric pair; count-0 array emits no stores, so byte-id-neutral. - selfhost/cmd/wcc/check.ww exprtype N_ARRLIT: default the inferred element's untyped flavor to concrete (untyped_int->int, _float->f64, _str->str, _rune->rune, _bool->bool, mirror cstage type_default), empty-elt "i32"->"int", and stamp the synthesized N_TARRAY's .type_ so slotsize / elemsizeofc / letslotsize read its real [N]int size via the type table (rule-13) — no letslotsize special-case (SSoT). combined.ww regen (check.ww embed): w6c + wwdump. ken v2 corpus re-census (160 files): EXACTLY 5 rows move, ALL CONVERGE (byte-id YES + run exit 0, none both-wrong, zero regression): m2_while #108 scalar via alias-bool loop m8_range1 #104 for-range elem over alias [4]int m8_range2 #104 over 2-level alias m8_slice1 #103 inferred array + alias-slice init m8_slice2 #103 + 2-level-alias slice + re-slice Bootstrap byte-id neutral (5 combined units w6c==w6c_ww; 0 bare inferred arrays in selfhost). Annotated controls untouched ([4]i32 stride-4, [4]int stride-8, byte-id). Pinned in test/wcc/813_arrlit_infer_elem_run (the 2 direct repros incl the >2^31 truncation teeth + all 5 movers + controls; test-unit 296). Closes #103 (inferred-array SEGV + truncation), #108 (cstage scalar untyped-int truncation), #104 (for-range elem alias i32-stamp), and the m8_slice []int-init acceptance divergence. |
|||
| fc50a27f3e |
cgen: #95 c3 reviewer-fold — is/as gate exact-only, no widening leak
c1/c2 widened flatvariantidxt (selfhost) with the chain + structural tag-synthesis arms and a >=2 ambiguity os.exit, scoped to the cgen WIDEN consumer. But flatvariantidxt is a choke-point: the wwstage is/as ACCEPTANCE gate (check.ww:4677, the #198 spread fallback) reuses it, so the widening leaked into checker acceptance — vs base |
|||
| 56aac85f6f |
cgen: #95 c2 structural variant fallback — both-stage fused
A nominally-unrelated, structurally-equal NAMED source into a NAMED
variant (kb95_unrel: ta/tb same-layout structs, src ta -> (void|tb))
was LIVE both-wrong-identical byte-id silent: both checkers accept,
both cgens tagged 0. After c1's chain arm finds no shared chain
node, match the variant whose CHASED type type_eq's/typeeq's the
source's chased bottom — chased type EQUALITY only, no
type_is_assignable scalar import, no int widening (ken's binding
scalar warning). Same NAMED-source branch, both stages
(cg_tag_for_variant / flatvariantidxt), forced fuse.
Correctness reference, cite 1 — harec tagged_select_subtype P2+P3
(ref/harec/src/types.c:702-739), verbatim:
if (t->id == subtype->id) {
return t;
}
if (type_is_assignable(ctx, t, subtype)) {
selected = t;
++nassign;
}
...
if (nassign == 1) {
return selected;
}
return NULL;
with type_is_assignable's non-tagged path dealiasing both sides and
accepting composites only via interned pointer equality
(types.c:988-1002), verbatim:
if (type_dealias(ctx, to)->storage != STORAGE_TAGGED) {
to = type_dealias(ctx, to);
from = type_dealias(ctx, from);
}
...
if (to == from && to->storage != STORAGE_VOID) {
return true;
}
Cite 2 — type_hash interns bare composites STRUCTURALLY (banked as
types.c:72-81; verified in the vendored copy at types.c:444 +
struct/union arm :514-525), verbatim:
case STORAGE_UNION:
hash = fnv1a_size(hash, type->struct_union.packed);
for (const struct struct_field *field = type->struct_union.fields;
field; field = field->next) {
if (field->name) {
hash = fnv1a_s(hash, field->name);
}
hash = fnv1a_u32(hash, type_hash(field->type));
hash = fnv1a_size(hash, field->offset);
}
— no decl ident in the hash, so harec's two decls dealias to ONE
interned node and `to == from` holds: acceptance is DEFINITIONAL
under interning, not an arm whose text could be misread. Our store
does not intern; chased type equality is the non-interned rendering
of the same rule.
Honest divergence (the >=2-structural-match hard-error STAYS): under
harec's interning two structurally-identical variants are ONE type —
a union cannot contain it twice — so the ambiguity case is
unrepresentable there; our hard-error (twin texts, shared tail
"source structurally matches >=2 variants — ambiguous without
nominal layout (#95)") is the correct nominal-lossy-model rendering,
not a harec deviation.
Pin table: unrel_struct row added (kb95_unrel graduates ok/1-ok/1 ->
0/0, byte-id held) — suite now 48/48. All c1 rows unmoved.
Invariants: 163-row dissolution matrix at tip — same 3 family
graduations as c1, ZERO new movers; five mains cs-vs-ww byte-id OK;
make all 0; sizelint 0; peellint 0 (no new peel sites — the
structural leg reads only chased ends); all 944 suites + 808 green.
w6c_ww/wwdump_ww main.combined.ww regen'd.
|
|||
| 34c86bd681 |
cgen: #95 c1 chain-membership variant arm — both-stage fused
A NAMED struct source that was not pointer-identical to a NAMED variant fell through every pass of cg_tag_for_variant (cmd/w6c/ cgen.c) / flatvariantidxt (selfhost/cmd/wcc/cgenutil.ww) and the widen stored tag 0 — both stages, byte-identical, gate-blind: wrong tag on VALID code at any alias depth, in both chain directions (.ai/ken-95-oracle.md §2: kb5_v2s1i, kb95_2lvl_i, kb95_deep_src, kb95_deep_var all both-wrong-identical at base). New pass 1b, identical both stages (the same route — forced fuse): after pass-1 exact (unchanged, FIRST — the (str|linerr) protection, harec's P1 short-circuit), a NAMED source matches the variant whose NAMED chain shares a pointer-identical node with the source's chain (an alias IS-A its base through the chain). Two linear NAMED chains intersect iff they share their chased bottom node (ken §1), so the walk is implemented as pointer identity of the chased ends through type_chase_named/tichase — the blessed chase choke-point. NO raw .under/->under hops were added, so the anticipated `peel-ok: nominal chain walk (#95)` annotations are unnecessary and the peellint whitelist is UNCHANGED (continues the B6/B7 fold-peels-into-chase arc; peellint green). Variants are counted UNGATED (bare prims are type-table singletons, so a bare variant node can BE the source's chased bottom): the >=2 guard stays equivalent to harec's nassign>=2 -> NULL (ref/harec/src/types.c:734-738, tagged_select_subtype P2/P3). >=2 chain hits hard-error with twin texts (prefix convention, shared tail "source alias chain reaches >=2 variants — ambiguous without nominal layout (#95)") — drew's ambiguity proviso extended to the chained set; was a SILENT member-0 tag. Pass-2 bare-source fallback unchanged. Chased type EQUALITY only — no type_is_assignable scalar import, no int widening (ken's binding scalar warning). Pin table (new suite test/wcc/944_variant_chain_b95_run.c, 45 checks, Makefile-wired): GRADUATIONS exit 1->0 both stages: chain_1lvl_i (kb5_v2s1i HEADLINE, byte-id held), chain_2lvl_i, chain_deep_src, chain_deep_var (byte-id held), chain_call_bound81 (kb5_v2s1), chain_call2_bound81 (kb4_v2_struct2, #95's original) — the two CALL-src rows waive byte-id, pre-existing #81 zero-fill asm noise (NO at base too). NEW LOUD: chain_amb_loud (kb95_amb) — silent tag 0 -> hard-error both stages. MUST-NOT-MOVE held: chain_amb_srcA/B (pass-1 precedence), nom_str/nom_err (#218 nominal regression pin), exact_ctl (kb5_v2sE2), bare_ctl/bare_2lvl/bare_ambig/bare_ambig2 (pass-2 controls), callret_bound277 (kb5_v2sE #277 cells unchanged, dual-cell pin). Invariants: ken's 163-row dissolution matrix rerun — exactly 3 movers, all family graduations (v2s1i/v2s1/v2_struct2 1->0), zero non-family movers, detectors unmoved. Five mains cs-vs-ww byte-id OK (ww/w6c/w6a/w6l/wwdump). make all 0; sizelint 0; peellint 0; all 944 suites + 808 green. w6c_ww/wwdump_ww main.combined.ww regen'd (cgenutil.ww embeds). |
|||
| 329481c920 |
wcc_ww/check: W3 #105 nested-arrlit gate chases the alias elem type
checkarrlitfits' nested recursion keyed on the raw elemtn kind; a named-alias element type ([2]row, row=[2]int) arrives as N_TNAME, so the inner overlong literal skipped the count+range checks and the module static-DATA route emitted silently TRUNCATED data (ken's m7c_global: DATAW 1,2,4,5 — exit-masked once the #60 read fix removed the segv; cstage loud-rejects every spelling via its typed-literal assignability net). #105: the W1 fill gate never runs on this route, severity raised post-#60. Fix: chase elemtn through resolvealias (transitive) at the recursion gate — alias spellings of any depth take the same checks as the direct shape at all four contexts funneling through the choke point (module let / local let / def / struct-field). A direct N_TARRAY passes through resolvealias unchanged, so accepted shapes are byte-identical base→tip (m7c_global_ok + exact-fit alias field/def/2lvl probed ASM-ID vs a base scratch build). The m7/m7b local overlong rows stay loud, now via the earlier count-naming checker diagnostic instead of the cgen #270-1c fatal. The out-of-range narrow inner element louds "array element out of range" exactly as the direct spelling already did on wwstage. 808_arrlit_overlong: 37 -> 50 checks (+1 accept control alias_exact_module = ken's m7c_global_ok with a byte-id cell, +4 loud flips alias_nested_{module,2lvl,def,field} pinning per-stage texts, +1 REVIEW AMENDMENT alias_nested_local pinning the m7/m7b text move — pre-fix ww was loud via the late cgen #270-1c fatal; the row reds if the diag regresses off the checker count text). 989 ratchet zero flips — no lib module-level literal trips the gate. Filed sibling, not folded: OUTER alias-of-array overlong (let g: arr = [5 elems], arr=[4]int) still ww-silent-truncates at the alias-blind call-site N_TARRAY gates; cs louds with the count text. |
|||
| 4b118fa8f8 |
cgen: B7 emitter elem chases + tools/peellint gate — #5 alias-arc cs side closed by construction
The last four raw `->under` reads outside the whitelist were the
static-DATA emitters' ELEMENT-type single peels (the outer type already
chased): emit_array_lit_bytes:14356, emit_strarray_data:14574,
emit_slice_data:14788, let_pre_intern:15088 -> type_chase_named.
:15088 is the :14574 row's label-order leg and must flip in the same
commit or _S_ labels intern in emit order, not decl order (the in-tree
comment at the site); the strarr row's byte-id is the coupling proof.
Behavior moves (ken B7 first-position oracle + impl pre-state, all
pre-observed at
|
|||
| 05f7af76d1 |
wcc_ww/cgen: W2 #102 indexed-elem field store + &-field gates chase the alias chain
The two ww-side gates from ken's B6-c3 STOP re-attribution
(.ai/ken-b6-oracle.md addendum): WWSTAGE was the alias-blind side at
exactly two cgenexpr.ww sites, both keyed on a bare name-keyed
structlookup that only holds struct-decl names, so an alias-NAMED
spelling missed and fell to the generic-but-runtime-correct route —
byte-divergent from the canonical dedicated shape cs pins post-B6-c3:
(1) `arr[i].f = v` indexed-elem struct-field STORE arm (the write
twin of the task-#8 READ arm, which already chases via tichase);
(2) `&p.f` ptr-field fallback in the TK_AMP N_DOT single-dot leg.
REVIEW AMENDMENT (reviewer-W2, rob's close-by-construction
obligation): the proof-sweep of the two arms found ONE same-class
survivor — (3) the value-struct `&x.f` leg, one leg below (2) in the
same single-DOT arm, same alias-blind bare structlookup. Probe
rW2_ampv reproduced the identical bit-proven mechanism (ww_alias !=
ww_plain; cs_alias == ww_plain) at both base and the frozen tip; the
other in-arm routes are clean (letvar* global helpers chase, str/len
pseudo-field alias legs are filed-loud #96, chained depth>=2 resolves
via tinfo). Same chase applied; arm survivor count is now zero by
the same grep+probe construction.
All three now resolve through structlookupchain (#22) — the name
layer's own alias-chain accessor (the cstage transitive-peel mirror),
already consumed by the sibling `p.f = v` assign arm and cgdot's
pointer-to-struct read. Plain rows short-circuit at its structlookup
head, so non-alias emission is byte-identical by construction; alias
rows land on the same structinfo as their plain twins, so the
dedicated emission converges bit-exactly. esz stays sound:
elemsizeofc reads the chased stamped tinfo (#8 leg).
Convergence proven with ken's pre-staged W2 instrument: ww_alias ==
ww_plain bit-IDENTICAL for kb6_idxf and kb6_ampf (cmp exit 0), which
also lands cs==ww byte-id on both rows; plain controls byte-id held
and their ww asm unchanged base->tip. Full ken-corpus matrix vs the
B6-tip baseline: movers are exactly {kb6_idxf, kb6_ampf} NO->YES;
detectors unmoved (kw1_101 cs0/ww1, fill2 both-loud, tuparg_c
cs0/ww1, fsarg2/fsarg0/try pinned texts verbatim, B5/B6 graduations
all byte-id). Zero behavioral change on any accepted program, zero
acceptance change in either direction.
ww-only: zero C-source bytes; cs binaries (w6c/ww/w6a/w6l) md5
bit-identical base->tip; w6c_ww + wwdump main.combined.ww regen'd via
make, idempotent. 989 lib ratchet: zero flips (31 byte-identical /
9 pinned-divergent #59 / 3 pinned-wwreject #59 across 43 units).
944_alias_cgen_b6_run: idxf_2lvl + ampf_2lvl graduate K_RUN_NOID ->
K_RUN (the two restored byte-id cells) — B6-c3's mutation teeth
restored (reviewer-B6 N-a closes); + amendment rows ampv_2lvl /
ampv_plain_ctl pin the third gate (checks 61 -> 69). All 9 944-family
suites green; sizelint 0; make test-unit "all 292 tests passed".
|
|||
| 4cac1cbb89 |
wcc_ww/cgen: W1 #100 struct-lit fill gate keys off the chased stamped tinfo
The ww twin of B5-c1's :3195 cg_structlit_fill chase — closes the R4-blessed one-train acceptance divergence (cs-loud / ww-SILENT-WRONG on alias-element array fills, ken FLAG-3 / kb5_fill2). The #249 fill arm's loud-gate element classification was alias-blind on two of four keys: a raw N_TSLICE kind test and a structlookup leaf-name probe — `type el = el0;` matched neither, bypassed the gate, and fell to the scalar tail (one word per element, silent exit 1, gate-blind #263 class). Now: issliceel via isslicetype (stamped tinfo, the str/tagged keys' existing route) and isstructel via tichase + TY_STRUCT kind — the tnodeisagg discipline the neighboring #49 arm already uses, twin of cstage type_chase_named at cgen.c:3179. NO new helper. Loudness-alignment only, zero acceptance movement elsewhere: probe matrix holds direct str/slice/struct/tagged elem LOUD (twin texts), int/alias-int/f64 elem fills build + run 0/0 byte-id, 1- and 2-level alias struct/slice/tagged elems now LOUD both stages. ww-only: cs side untouched (zero C bytes; w6c up-to-date across the edit). B5 detector rows hold post-B5 expected state (targ/tmem/wpush/ null/succ/f32p/def93 all 0/0 byte-id). 989 ratchet ZERO flips (31 ID / 9 pinned-divergent / 3 pinned-wwreject). combined.ww regen'd for both embedded targets (w6c_ww, wwdump). 944_alias_cgen_b5_run: fill2 graduates the dual-cell K_CSERR_WWRUN pin to the K_BUILDERR LOUD-HOLD pair (fill0-class, shared experr tail); fill0 control holds. Check count holds at 84/84 — the kind flip is cell-count-neutral (2 driver cells either side, no byte-id cell). All 944-family suites green; sizelint 0; test-unit all 291 passed. Closes #100; the R4 divergence window closes with it. |
|||
| bcd948de88 |
wcc_ww/check: c4 #80 bare-binder forrange dealiases the iterable's type expr
F2a batch-4 c4. RE-PROBED AFTER c1 per spec: still live at the c3 train base with a REBUILT w6c_ww (the mechanical tichase collapse didn't cover it — this read is AST-node-keyed, not tinfo-keyed). REPRO (.ai/scratch/repro_f2a1_b4.ww): `untyped_lit * rangevar` over a range-for of an alias-typed slice (`type slk = []int`) — wwstage checker dies "asserttyped: bin" at the binop; cs accepts and runs 0. TRACE: check.ww resolvewalk N_FORRANGE bare-binder arm — the binder's element type comes from kind-testing the scrutinee's type expr (N_TSLICE/N_TARRAY), but an alias-typed iterable arrives as N_TNAME: both tests miss, the binder falls to the N_FORRANGE fallback decl, stays untyped, and the first binop over it bails. cs twin types the binding at scope_define (check.c N_FORRANGE) — accepts. FIX (single site, the one the repro traces to, per grant): dealias via the existing resolvealias(unwrapbang(it)) idiom before the kind tests. The TUPLE-DESTRUCTURE arm carries the same unresolved tests but is NOT in-grant (fixing the bare arm is not a no-op, so the re-spelling clause does not apply) — FILED as task #97; currently double-masked bounded-loud (cs louds upstream at #270-1c so the alias-tuple-slice iterable is unconstructible on cs; ww asserttyped). Rows (944): rangevar_alias2 (the repro, cs0/ww-reject -> 0/0 byte-id) + rangevar_plain_ctl (non-alias control, held throughout). 944 202/202. Corpus: five-mains ww NEUTRAL vs base on identical inputs (checker-acceptance-only change; no alias range-for in corpus). combined.ww regens ride along. |
|||
| 10d886906f |
wcc_ww/check: c3 spread-member + &len/cap base walks chase the full alias chain
F2a batch-4 c3, the check.ww behavior pair (exact set). Both cs twins
ALREADY full-chase at this SHA — ww-only align-up, no fuse:
B1 (check.ww:2038 census line; peel was at :2041 post-c1): the
`...inner` spread member peeled ONE level, so a 2-level-alias inner
union escaped the TY_TAGGED splice test and rode as a SURFACE member —
the outer box sized off the inner union's own header (probe sp_alias2:
runtime 0/0 BOTH but byte-DIVERGE, ww frame $48 vs $32). cs twin
chases (check.c:755 type_chase_named, the spec's :660-667 cite moved).
Fix: vu = tichase(vu) under the isspread gate, mirroring cs's
`spread ? type_chase_named(vt) : vt`. sp_alias2 graduates 0/0 byte-id;
sp_alias1 1-level control held throughout.
B2 (check.ww:2273/:2275): the &x.len/&x.cap base walk hand-peeled
NAMED->PTR->NAMED one level per hop — 2-level alias bases fell out of
the slice/str detect and took the generic *opt typing. cs twin chases
both hops (check.c:1198-1201). Fix: tichase per hop, exact cs mirror.
B2 ROUTE-TRACE (flagged, not edited): the spec's "post 0/0" cell for
amplen2 is NOT achievable inside this grant — OBSERVED pre-fix that
ww dies LATER at the cgen ADDRESS tail ("unsupported address-of
shape") for ALL alias bases 1+ LEVEL (cs runs 0): task #96 (ken
b4-oracle), a cgenexpr.ww site outside c3's check.ww set. The chase
here aligns the stamped type only (latent until #96); rows pinned
LOUD-HOLD via the new K_RUN_CS_WWERR kind (cs builds+runs; the ww
build must keep FAILING loud with the pinned diagnostic — a silent
ww accept-and-run is the regression the bound row exists to catch),
citing #96; they graduate K_RUN with it. Also OBSERVED: the let-init
spelling (`let s: sl1 = [1,2,3]`) is blocked further upstream by the
rows use the param route.
Pin rows (944): sp_alias1 K_RUN hold, sp_alias2 K_RUN graduation,
amplen_plain K_RUN control hold, amplen1/amplen2/ampcap2
K_RUN_CS_WWERR LOUD-HOLD bound on #96 (ww leg asserts "unsupported
address-of shape"). 944: 196/196.
Corpus: five-mains NEUTRAL vs base on identical inputs, cs==ww holds
(both clusters zero-in-corpus). combined.ww regens ride along.
|
|||
| 4e174099bd |
cgen: c2 variant pass-2 structural fallback chases the full NAMED chain — both stages fused
F2a batch-4 c2. Site: cgenutil.ww flatvariantidxt pass-2 (was :2895 at |
|||
| b26b3dbdc5 |
wcc_ww: c1 collapse plain TY_NAMED chase loops into tichase across cgenstmt/cgen/check — byte-id neutral
F2a batch-4 c1, mechanical, per rob's spec census (re-verified at
4adf914; check.ww/cgenstmt.ww cites were stable as predicted, the
two cgenutil [B3-SHIFT] cites moved +8: :2746->:2754, :2895->:2903).
42 conversions, every site eyes-classified plain nil+NAMED:
- cgenstmt.ww 26: one-line :149 :325 :352 :532 :551 :594 :715 :732
:938 :940 :1577 :1629 :1782 :2230 :2575 :3372 :3678 + multi-line
:986 :1912 :2287 :2291 :2702 :2759 :2823 :3037 :3715 (all plain,
no extra guards).
- cgen.ww 13: one-line :1505 :1541 :1658 :1691 :1697 :1947 :1951
:2065 :2109 :2118 + multi-line :2239 :2648 :2663 (incl. the
emitdefconstants struct/array arms — plain).
- check.ww 3: :1627 tupleelemslot, :1673 fieldslotsize, :4111 arrlit
elem chase; WHY comments kept. tichase visibility verified: same
`package wcc` (cgenstmt/cgen already call it) — no second helper.
Survivors (classified, untouched):
- cgenutil.ww :1301 tichase's own body (the accessor) + :2754
nullableptrtag single peel (batch-2 c3-B2 PROBE-CLEARED,
|
|||
| 4adf914f2f |
wcc_ww/cgen: #92 rhsstructpayload N_STRUCTLIT arm through structlookupchain
The N_STRUCTLIT arm still did bare structlookup on the literal's type
name — an alias-named struct LITERAL widened into a union (`type ali =
base; let v: (void|ali) = ali{...}`) missed the registered structinfo
and fell to the scalar widen arm: word0-only/dropped payload, cs 0 /
ww EXIT 1, byte-id NO (reviewer-B2 find, pre-existing at 486f7f8's
parent). Exactly the class batch-2 c2 closed for the N_IDENT-local arm
of the SAME function; same funnel fix — route through structlookupchain
(cgenutil.ww:1758) and return si.sname so every consumer's re-lookup
hits the REGISTERED name.
One pre-authorized rider: structlookupchain's entry gate accepted only
N_TNAME, but a struct literal's type ref parses as N_IDENT (expression
position, lib/ww/parse/expr.ww builds s.lhs as N_IDENT — OBSERVED).
Widened to `N_TNAME || N_IDENT`; the entry-kind predicate is spelled
twice (the early-return and the chain loop's first iteration — the
loop reassigns cur to aliaslookup results, which are always N_TNAME,
so later iterations are untouched). Consumer census, all 10 call
sites: cgenutil:3187(+this arm; post-commit position), cgenexpr:2927/3006/8775/9011,
cgenstmt:3033 guard on N_TNAME explicitly before calling;
cgenexpr:5756 (etnode = type-spec .lhs), cgenstmt:1978 (arrtn.lhs or
the synthesized #79 N_TNAME), cgenstmt:2695 (cglet n.lhs declared
type spec) pass parsetype products only — parsetype builds zero
N_IDENT nodes (lib/ww/parse/parse.ww). No existing caller can pass
N_IDENT: the widening is strictly accepting-more; existing callers
unmoved. NO second inline chase in the arm (close-by-construction).
Base spellings byte-id: structlookup hits at the chain entry and
si.sname == the literal's own name — same string out, same asm.
Pin: 944_alias_accept_run union_slit_{base_ctl,alias,alias2,order} —
base control holds 0/0; 1/2-level alias + permuted decl order graduate
cs0/ww1-byte-id-NO -> 0/0 byte-id. #63 PROBED post-c3 per the
enrollment rule: does NOT green at this site (8B alias struct-lit
let-init still ww silent-zero-fill exit 1; 16B still loud "aggregate
init from unhandled rhs shape") — its miss is the cgenstmt let-init
dispatch, not rhsstructpayload; documented on task #63, left red, no
row enrolled.
Light gates: test-unit 290 green; sizelint 0; 989 ratchet zero flips;
five-mains NEUTRAL vs master-74195ac scratch on identical inputs +
cs==ww on all five. combined.ww regens ride along (#110).
|
|||
| 3e9a6955e7 |
wcc_ww/cgen: #88 defisaddressable array leg chases the stamped def type
The `&D` addressability gate (defisaddressable, cgen.ww) keyed its array leg on the UNCHASED syntactic dtnode (N_TARRAY) — a def whose declared type is an ALIAS of an array missed the gate and fell to the rule-7 loud error, but the gate was lying: the ww def-array DATA emitter (emitdefconstants' array arm) already peels TY_NAMED off d.lhs.type_ transitively, so the alias def HAS a DATA symbol (probe-OBSERVED: `DATA main.D(SB)` emitted byte-id by both stages for the &-less program). Gate-only fix — tichase(dtn.type_) == TY_ARRAY — restores gate == emission set exactly; no emitter twin, no half-state. The struct leg (defvarstructinfo) already chased; plain [N]T defs agree under tnode and chased reads, so existing rows are byte-id-neutral by construction. cstage gates TK_AMP on the def_isarraydef registry fed by the g-fold-G1 chased let_isarray (cgen.c:3975-3977, 1446) and runs every row 0 — align ww UP. Pin: 944_alias_def_addr_run, 6 rows (plain + struct-def controls hold 0/0; 1/2-level alias + fwd-ref decl order graduate ww-LOUD -> 0/0 byte-id; str-def &S error-path STAYS LOUD both stages with a byte-identical diagnostic — the rule-7 tail text is compared w6c vs w6c_ww, so a silent reject or a divergent message both fail the row). def_l2's readback casts to the base array ptr: the natural (*p)[2] spelling over a 2-LEVEL-alias pointee trips a SEPARATE pre-existing CSTAGE double-deref (spurious MOVQ (AX),AX, SEGV; def-independent, ww correct) — filed as task #93 (#85 type_unwrap kin, F2b OUT), not fixed here (site-set form). Light gates: test-unit 290 green; sizelint 0; 989 ratchet zero flips; five-mains NEUTRAL vs master-74195ac scratch on identical inputs + cs==ww on all five. combined.ww regens ride along (#110). |
|||
| d5cb1bd69e |
wcc_ww/cgen: #82 cgun &base[i] classify off the chased stamped base type
The TK_AMP N_INDEX arm keyed arrayness off the SYNTACTIC tnode (local leg isarr at the baselocal read; global leg isglobalarr/isglobalptr at the letvartnode read) — an alias-typed base (tnode N_TNAME) missed the N_TARRAY gate, so the base materialized as MOVQ (element-0 VALUE) instead of LEAQ (storage address): wild pointer, SEGV/corruption on the deref. SILENT class (metric-1). The global leg graduated from latent to live when g-fold #77/#78 landed alias-global DATA emit. Fix re-keys both legs off tichase(base.type_) gated on TY_NAMED — the landed cgindex #60 idiom (cgenexpr.ww:1800-1820). cstage already classifies off the chased type (type_chase_named, cmd/w6c/cgen.c: 4172-4188) and is the runtime-correct reference: align ww UP. esz does NOT move — elemsizeofc chases internally since batch-2 (PREMISE-2 probe-confirmed via amp_narrow: stride right, base wrong pre-fix). Non-alias rows byte-id-neutral by construction (TY_NAMED gate). Pin: 944_alias_amp_idx_run, 8 rows through the taken pointer (plain local/global+str controls hold 0/0; 1/2-level alias local + global, fwd-ref decl order, narrow [4]u32 graduate cs0/wwSEGV-byte-id-NO -> 0/0 byte-id). Probed OUT, filed not fixed (spec §1 NOTE-2): &D[i] def-array base breaks at a DIFFERENT site both stages (cs XORQ BX,BX zero-base cgen.c:4209-4212, ww complex-base fallback; both SEGV 139). Light gates: test-unit 289 green; sizelint 0; 989 ratchet zero flips (31 ID / 9 DIVERGE / 3 WWREJECT pins hold); five-mains NEUTRAL vs master-74195ac scratch build on identical inputs + cs==ww on all five. combined.ww regens ride along (#110). |
|||
| 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 (
|
|||
| 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). |
|||
| 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 ( |
|||
| 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
|
|||
| 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
|
|||
| 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
|
|||
| 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. |