wwstage: copy full tagged-element slot for N_DOT/N_INDEX-base index read (#261)

The #259 store fix unmasked a pre-existing latent cs!=ww in the tagged-
element READ via an N_DOT base (`x.o[i]`) / chained N_INDEX base
(`m[i][j]`): wwstage materialized the element as a SCALAR one-word load +
zeroed tag where cstage copies the full tagged slot — silently dropping
the tag/payload-high word (wrong variant). Three sites all keyed off the
same N_IDENT-only gate; cstage classifies TY_TAGGED for ANY base off the
checker-stamped element type. Align wwstage UP:

- cgindex (cgenexpr.ww): the N_DOT/N_INDEX-base arm now sets
  elem_tagged/elem_slot_sz from n.type_ (the stamped element tinfo),
  mirroring cstage cgen.c:8101 — the full-slot copy arms then fire.
- rhstaggedabicall (cgenutil.ww): the N_INDEX branch reads
  typeistagged(src.type_) for any base instead of an N_IDENT-only
  structural lookup, mirroring cstage's src->type keying — fixes the
  let-init / call-arg widen-source spill.
- forwardtagged (cgenstmt.ww): the return-path passthrough gate now
  accepts N_INDEX/N_DOT tagged rhs (which cgexpr materializes into the
  tagged ABI), not just N_CALL — fixes `return x.o[i]`.

read + call-arg + return + chained 2D all close by construction (one
materialization path). cstage unchanged (pure wwstage-align-up). 949
gains 9 #261 rows (i32 + explicit-void variant per shape proves the tag
survives) and flips the two #259 read-back rows to byteid=1.
This commit is contained in:
2026-06-02 06:02:56 +09:00
parent be23d7227a
commit 0afc272f47
6 changed files with 243 additions and 77 deletions

View File

@@ -630,8 +630,17 @@ fn cgreturn(c: *cgen, n: *node) void = {
// chain suffices because variants are primitives (single
// tctx tinfo) or NAMED (per-decl identity); a full recursive
// tinfo structural-eq helper is gated by #178.
// #261: N_INDEX of a tagged element (`return x.o[i]`) and
// N_DOT of a tagged field both materialize the full tagged
// ABI shape via cgexpr (cgindex slot-copy / cgdot field-load,
// AX=tag/DX=v0/...), exactly like an N_CALL of a tagged-
// returning fn — so a same-type return forwards them
// unchanged. cstage gates passthrough purely on the rhs type
// (no kind filter, cgen.c:8845); without these kinds an
// N_INDEX tagged-element return fell to the scalar-variant
// shuffle (MOVQ AX,DX; MOVQ $0,AX), dropping the payload.
let forwardtagged: bool = false;
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
if ((rhs.kind == nkind.N_CALL || rhs.kind == nkind.N_INDEX || rhs.kind == nkind.N_DOT) && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
let ru: *tinfo = rhs.type_: *tinfo;
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
let fu: *tinfo = c.fnret.type_: *tinfo;