`t[i].cap` (t a `[N][]u8` / `[N]str`) miscompiled in BOTH stages, divergently — the read-side sibling of #20's store fix. cgexpr on the indexed element leaves the full {ptr,len,cap} header (AX/BX/CX via cgslicehdr), but the `.cap` field-selector never shuffled CX→AX: cstage's typed pseudo-field else-branch handled only .ptr/.len, so `.cap` fell through returning AX=.ptr; wwstage's cgdot non-ident catch-all likewise handled only .ptr/.len, emitting no read (stale AX). `t[i].len` already worked (BX→AX shuffle) — only `.cap` was missing. Fix mirrors the .len shuffle: add the .cap CX→AX arm in both stages. The shuffle fires ONLY for a typed slice/str base (TY_SLICE/TY_STR after NAMED-chase); an untyped str literal (`"abc".cap`) leaves only AX=ptr/BX=len and must return AX unshuffled — keeping the wwstage catch-all byte-identical with cstage, whose cap-shuffle lives in the typed branch, not the untyped catch-all. Validated direct `t[i].cap` (slice + str, elements 0/1) against the whole-element-copy oracle (`let q=t[i]; q.cap`, made correct by #20), plus .len-after-index regression pins, in test 683; dual-stage runtime + byte-id (36/36 ok). combined.ww regenerated.
This commit is contained in:
@@ -22611,6 +22611,26 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
return;
|
||||
};
|
||||
// .cap on a non-ident base (indexed element `t[i].cap`, call,
|
||||
// dot-slice): cgexpr leaves the full {ptr,len,cap} header via
|
||||
// cgslicehdr — shuffle CX→AX. The shuffle fires ONLY for a TYPED
|
||||
// slice/str base (kind TY_SLICE/TY_STR after NAMED-chase); an untyped
|
||||
// str literal (`"abc".cap`) leaves only AX=ptr/BX=len and must return
|
||||
// AX unshuffled. Mirrors cstage cgen.c: the cap-shuffle lives in the
|
||||
// typed pseudo-field branch (`u->kind == TY_SLICE/TY_STR`), never the
|
||||
// untyped catch-all. #13 read-fix, sibling of the #20 store.
|
||||
if (streq(fld, "cap")) {
|
||||
cgexpr(c, lhs);
|
||||
if (lhs != nil) {
|
||||
let lu: *tinfo = lhs.type_: *tinfo;
|
||||
for (lu != nil && lu.kind == tykind.TY_NAMED) { lu = lu.under; };
|
||||
if (lu != nil && (lu.kind == tykind.TY_SLICE
|
||||
|| lu.kind == tykind.TY_STR)) {
|
||||
emitline("\tMOVQ\tCX, AX\n");
|
||||
};
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Chained struct-field-via-ptr-via-ptr access:
|
||||
// r.sym.val where r: *lrel, .sym: *lsym, .val: u64
|
||||
// Inner DOT (`r.sym`) returns a *struct (a pointer-to-struct
|
||||
|
||||
Reference in New Issue
Block a user