wcc/cgen: #121 indexed tuple-element read + literal-store round-trip (both-stage)

Reading or storing a tuple element of an indexed array element was
broken across the board (the fold-6 read-path). One fused commit,
both stages, four faces of indexed tuple-element access:

 - FIELD read `tbl[i].N`: was loud ("unsupported field-read shape" --
   the field-read dispatch keyed on an N_IDENT base; an INDEX base fell
   to a fatal). Now resolves &tbl[i] via the place-spine and reads the
   field at addr+foff through the existing per-kind arms (str-triple /
   scalar / fn-ptr).
 - WHOLE read `let e = tbl[i]`: was a silent word0-only truncation
   (plain-tuple kin of #37/#58, which covered only tagged). Now a full
   cursor fill from &tbl[i].
 - STORE `a[i] = (3,4)` (N_TUPLE-literal rhs): was a silent word0-only
   store -- the write face of the read. The aggregate-store-into-index
   site handled ident/dot/deref tuple rhs but not the literal; now it
   materializes the literal and word-copies. Narrow: N_IDENT base only
   (N_DOT/chained stay deferred, #270).
 - for-range over a const-slice-of-tuple: was a divergent SEGV; now a
   symmetric loud-stop on both stages (filed #122).

The store and read were a round-trip that passed test 809 only by luck
(broken store XOR broken read canceled). Fixing the read alone exposed
the silent store; rule-7 obliges fixing both, so 809 is now genuinely
correct, not luck-correct. Both faces are byte-id-blind (#263) -- the
net is a runtime round-trip pin with distinct-per-word values and a
real call clobbering the cursor registers between store and read, so a
word0-only store or read is caught. Both stages byte-identical
(990-997 green). Pin 947_tuple_index_read_run.
This commit is contained in:
2026-06-06 22:23:43 +09:00
parent 761cfa4524
commit 754944a755
8 changed files with 1317 additions and 4 deletions

View File

@@ -3727,6 +3727,32 @@ fn cgforrange(c: *cgen, n: *node) void = {
baseoff = localalloc(c, bname, 8, nil);
};
};
// #121 leg (c): for-range over a module-GLOBAL slice/str/array base
// SEGV's today — the init + per-iteration base resolution below
// assume a frame-local slot (localfindnode), so a global let/def base
// reads saved-BP as the .ptr/.len. LOUD-STOP symmetric with cstage
// cgen.c (byte-id-neutral; segfault→compile-error is pure
// improvement). The fix (the N_INDEX isglobal base resolution ported
// into the for-range spine) is a DISTINCT mechanism — filed as a #121
// sibling, off fold-6's path.
if (slc != nil) {
if (slc.kind == nkind.N_IDENT) {
if (localfindnode(c, slc.str) == nil) {
let isglob: bool = isletvar(c, slc.str);
if (!isglob) {
let gdtn: *node = defvartnode(c, slc.str);
if (gdtn != nil) {
if (gdtn.kind == nkind.N_TARRAY) { isglob = true; };
};
};
if (isglob) {
let mc: str = "#121: for-range over a module-global slice/array base unwired (global-base resolution gap)\n";
os.write(2, mc.ptr, mc.len: u64);
os.exit(1);
};
};
};
};
// Per-binding (up to 8 — matches the C array). Parallel arrays so
// we don't depend on local-struct cgen.