wcc/ww: nodeisslice/nodeisstr gain the N_INDEX arm (stamp-keyed)
A slice/str ELEMENT of an indexed expression passed as a call-arg pushed one word instead of the 24B/16B header — the predicates had no N_INDEX arm, so element-typed args fell to the scalar path (review findings #45/#46). Read the element-type stamp; dual-stage rows in 989_idxarg_run pin cs==ww (red 2/8 on pre-fix binaries).
This commit is contained in:
@@ -1214,6 +1214,17 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
if (k == nkind.N_DOT) {
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
// #45 (F7-c2): `arr[i]` whose element is a slice. cgindex leaves
|
||||
// (AX=ptr, BX=len, CX=cap) for a 24B element, but with no N_INDEX
|
||||
// arm here pushargsrev fell to the scalar default — one PUSHQ AX —
|
||||
// and the cgcall pop under-drained by 2 words (take(rows[1]) pushed
|
||||
// 1 word, callee read garbage len). Read the checker-stamped element
|
||||
// type (exprtype N_INDEX stamps n.type_, check.ww:2777), mirroring
|
||||
// cstage node_isslice = type_isslice(n->type) and the N_INDEX arms of
|
||||
// nodeisstr (below) + nodeisunsigned (:1798).
|
||||
if (k == nkind.N_INDEX) {
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1221,14 +1232,14 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
// evaluate to a str value? Used to drive the call-arg push convention
|
||||
// (str args take two slots: ptr + len).
|
||||
//
|
||||
// TODO(#11): every consumer of "is-str" here reconstructs the answer
|
||||
// from raw N_kind because wwstage has no typed AST. Each new expression
|
||||
// shape needs an explicit arm or it silently falls through to false,
|
||||
// which downstream drops the second slot (BX/len) at the call site.
|
||||
// A typed AST check (cstage reads n->type) would replace this whole
|
||||
// function. Covered arms below: N_STRLIT, N_IDENT (local/let-typed),
|
||||
// N_CALL (return type), N_INDEX (element type of [N]T / []T / *T base),
|
||||
// N_DOT (reads checker-stamped n.type_ — #56 A.6.3h), N_CAST.
|
||||
// The value-bearing arms (N_IDENT local, N_INDEX, N_DOT) read the
|
||||
// checker-stamped n.type_ — the typed-AST check the prior TODO(#11)
|
||||
// wanted, mirroring cstage node_isstr = type_isstr(n->type). The
|
||||
// remaining N_kind arms (N_STRLIT, N_CALL, N_CAST) carry their own
|
||||
// recognizer because the stamp is on a sub-node (callee return / cast
|
||||
// target), not on `n` itself. Covered: N_STRLIT, N_IDENT (local stamp /
|
||||
// def stamp), N_CALL (return type), N_INDEX (element stamp — #46 F7-c2),
|
||||
// N_DOT (n.type_ — #56 A.6.3h), N_CAST.
|
||||
// Not covered (separate bugs / out of scope):
|
||||
// - N_UN(TK_STAR) of `*str` — cgun itself emits only `MOVQ (AX), AX`
|
||||
// and never loads .len into BX; fixing the recognizer alone won't
|
||||
@@ -1290,82 +1301,17 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// N_INDEX: `arr[i]` whose base is an indexable type carrying a
|
||||
// str element. cgindex correctly loads (AX=ptr, BX=len) for a
|
||||
// 16B element; without this arm pushargsrev only pushes AX and
|
||||
// the call-arg pop reads .len from stack residue. Mirror of
|
||||
// cstage's node_isstr → type_isstr(n->type), where n->type is
|
||||
// the resolved element type after check.
|
||||
// #46 (F7-c2): `arr[i]` whose element is a str. The prior structural
|
||||
// walk only recognised N_IDENT and N_DOT bases (idxelemtn off the
|
||||
// base's tnode), so a chained / call / slice base (take(m[1][1]))
|
||||
// fell through to `return false` → 1-word push, callee read garbage
|
||||
// .len. Read the checker-stamped element type instead (exprtype
|
||||
// N_INDEX stamps n.type_, check.ww:2777), mirroring cstage node_isstr
|
||||
// = type_isstr(n->type) and nodeisunsigned's N_INDEX arm (:1798). The
|
||||
// base-kind whitelist is gone — every base shape routes through the
|
||||
// one stamp read.
|
||||
if (k == nkind.N_INDEX) {
|
||||
let base: *node = n.lhs;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let bt: *node = nil;
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) { bt = lc.tnode; }
|
||||
else { bt = letvartnode(c, base.str); };
|
||||
if (bt != nil) {
|
||||
// idxelemtn: `*[N]T` drills to the pointee
|
||||
// array's element (#61).
|
||||
let elem: *node = idxelemtn(bt);
|
||||
if (elem != nil) {
|
||||
return isstrtype(c, elem);
|
||||
};
|
||||
};
|
||||
};
|
||||
// N_INDEX through a struct field: e.g. cmd.argsptr[i]
|
||||
// where argsptr: *str. cgindex correctly loads the
|
||||
// (ptr, len) pair off the stamped element size; without
|
||||
// this arm pushargsrev would only push AX and lose .len.
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let fld: str = base.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
let inner: *node = base.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, inner.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pinner: *node = tn.lhs;
|
||||
if (pinner != nil) {
|
||||
if (pinner.kind == nkind.N_TNAME) {
|
||||
sname = pinner.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fld)) {
|
||||
let ft: *node = fi.tnode;
|
||||
if (ft != nil) {
|
||||
// idxelemtn: `*[N]T` drill (#61).
|
||||
let elem: *node = idxelemtn(ft);
|
||||
if (elem != nil) {
|
||||
return isstrtype(c, elem);
|
||||
};
|
||||
};
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
return typeisstr(n.type_: *tinfo);
|
||||
};
|
||||
// N_DOT: read the checker-stamped n.type_. Struct field, nested
|
||||
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
|
||||
|
||||
Reference in New Issue
Block a user