w6c+w6c_ww: *[N]T indexing strides by element, not whole array (#61 A+B)
Indexing through a pointer-to-array auto-derefs, so esz and the element classification must come from the pointee array's ELEMENT (cstage idx_eff semantics, cgen.c:1163). Two halves of one root class: A (wwstage-only, cs!=ww, cstage runtime-correct): elemsizeofc's #270-2 nested-array block treated an N_TPTR pointee-array like a [N][M]T outer index and returned the whole-array size — every p[i] read/write/ compound scaled by N*size(T), and the same wrong element belief reached the store-width chooser (var-idx write emitted an N*8B aggregate copy sourced at the 8B rhs slot: caller-frame smash, the siphash round() corruption). Fixed via two wwstage choke-points mirroring idx_eff: idxeffti (tinfo: NAMED peel + TY_PTR->TY_ARRAY drill; feeds elemsizeofc and elemissignedc/elemisfloatc/elemisf32c) and idxelemtn (node: element tnode with the same drill; feeds every cgindex/cgassign/nodeisstr/ match-scrutinee elemtn resolution). B (BOTH stages identically wrong, byte-id-BLIND): the TK_AMP &base[i] arm read bu->sub->size without the ptr peel (&p[3]-&a[0] = 96, not 24). cstage now routes esz through idx_eff. A and B are FUSED by the pre-existing routing topology, not by choice (rule 11): wwstage's TK_AMP arm already reads its esz via elemsizeofc (selfhost/cmd/wcc/cgenexpr.ww:4095, the #11 addr-of twin of the #10 cgindex fix), so fixing A's choke-point flips wwstage's half of B in the same stroke. A standalone A leaves &p[i] transiently cs!=ww; B-first is the mirror transient; carving the TK_AMP caller out of the fixed choke-point to preserve the wrong stride for one commit would be a deliberate known-wrong intermediate (rule-7, vetoed by rob). One choke-point, two enrolled routes — un-fusable without a red intermediate. Close-by-construction proof-grep (both stages): every remaining raw sub->size index-stride read is TY_ARRAY-gated, a slice-only builtin (delete/insert), a checker-stamped element tinfo (indexresult already decays *[N]T, check.ww:2277-2284), or a non-index context (tuple slots, let-init elements). Two true residuals filed with site+symptom instead of silently absorbed: N_SLICE through *[N]T does not decay (LOUD type error, Hare divergence; team task #18) and non-ident cast-expression index bases keep wwstage's 8B-default esz (pre-existing #74-style cluster; team task #19). cstage's N_INDEX read-side str/slice header gates also move from u->sub to esub (identical for every non-ptr-to-array base; honest for *[N]str — pre-fix BOTH stages were runtime-wrong there, differently). 949_ptrarr_index_run pins the class at runtime + byte-id: {1,2,4,8}B elems, const+var idx, param/local/cast bases, read/write/compound, neighbor guards, &p[i] pointer-difference, siphash-round mix shape. 989_lib_byteid: siphash_test graduates #59.7 DIVERGE -> ID (ratchet tripped loud pre-update; no other #59.x pin flipped in the same run). (*p)[i] (sub-bug C) follows separately.
This commit is contained in:
@@ -3826,14 +3826,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
/* Fall through to silent-drop fallback below. */
|
||||
}
|
||||
if (opnd && opnd->kind == N_INDEX) {
|
||||
/* &base[i] = base + i*esz, no dereference. */
|
||||
/* &base[i] = base + i*esz, no dereference.
|
||||
* esz via idx_eff (#61): `&p[i]` on `p: *[N]T`
|
||||
* strides the pointee array's ELEMENT — the
|
||||
* undrilled bu->sub here was the whole [N]T
|
||||
* (&p[i]-&p[0] = i*N*size(T), wild pointer).
|
||||
* Base load still keys off bu (is_arr stays
|
||||
* false for the ptr → MOVQ of p's value). */
|
||||
Node *base = opnd->lhs;
|
||||
Node *idx = opnd->rhs;
|
||||
Type *bt = base ? base->type : NULL;
|
||||
Type *bu = (bt && bt->kind == TY_NAMED)
|
||||
? bt->under : bt;
|
||||
int esz = (bu && bu->sub)
|
||||
? (int)bu->sub->size : 1;
|
||||
Type *eff = idx_eff(bt);
|
||||
int esz = (eff && eff->sub)
|
||||
? (int)eff->sub->size : 1;
|
||||
cgexpr(c, idx, locals); /* idx → AX */
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz),
|
||||
@@ -10259,8 +10266,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* word must survive. Kind-gate on type_isstr||type_isslice,
|
||||
* never size==24: a >16B struct is 24B+ too but takes the
|
||||
* struct-copy path, not this 3-word header load (#10).
|
||||
* Base is BX. */
|
||||
if (u->sub && (type_isstr(u->sub) || type_isslice(u->sub))) {
|
||||
* Gate on esub (= idx_eff'd element, #61), not u->sub —
|
||||
* for `*[N]str` u->sub is the ARRAY and the gate missed,
|
||||
* falling to a 1-word load that dropped len/cap. esub ==
|
||||
* u->sub for every non-ptr-to-array base. Base is BX. */
|
||||
if (esub && (type_isstr(esub) || type_isslice(esub))) {
|
||||
cgslicehdr(c, D_BX);
|
||||
break;
|
||||
}
|
||||
@@ -10329,8 +10339,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
/* str/slice element via fallback base: load the full (ptr, len,
|
||||
* cap) header into (AX, BX, CX). Kind-gate on type_isstr||
|
||||
* type_isslice, never size==24 (see Site A). Base is AX. */
|
||||
if (u && u->sub && (type_isstr(u->sub) || type_isslice(u->sub))) {
|
||||
* type_isslice, never size==24 (see Site A). esub, not u->sub
|
||||
* (#61 — see the ident arm). Base is AX. */
|
||||
if (esub && (type_isstr(esub) || type_isslice(esub))) {
|
||||
cgslicehdr(c, D_AX);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1776,6 +1776,11 @@ cexpr(Checker *c, Node *n)
|
||||
return n->type = base;
|
||||
if (u && u->kind == TY_STR)
|
||||
return n->type = ty_str;
|
||||
/* Retained divergence: *[N]T does NOT decay here —
|
||||
* `p[lo:hi]` types as [][N]T (C-pointer-slicing), unlike
|
||||
* the index route (idx_eff) and unlike Hare. Loud on the
|
||||
* usual []T annotation; for-range likewise. Team task #18
|
||||
* (#61-residual A). */
|
||||
if (u && u->kind == TY_PTR && u->sub)
|
||||
return n->type = type_slice(c->a, u->sub);
|
||||
return n->type = err(c, n->pos, "cannot slice %s",
|
||||
|
||||
Reference in New Issue
Block a user