selfhost/cmd/wcc/cgenutil: collapse slice+str N_DOT arms (#56, A.6.3h)
nodeisslice + nodeisstr each walked base->struct->field on N_DOT,
gated on `base.kind == nkind.N_IDENT` (with dotinnerstructptr and
dotchainresolve fallbacks for chained / value-struct cases) --
duplicating cstage at the AST level while silently dropping
multi-level chains rooted at non-IDENT/DOT bases (e.g. `t.1.field`
for a tuple positional). After A.6.2 the checker stamps n.type_ on
every N_DOT (check.ww:1947-1996 pseudo-field, struct-field, and
tuple-positional arms), and A.6.3b landed typeisstr / typeisslice.
Both arms collapse to one tinfo read.
Polarity DOWN per rule 10: cstage was already aligned. cgen.c:168-170
node_isstr = type_isstr(n->type); cgen.c:182-184 node_isslice =
type_isslice(n->type). Wwstage was the laggard; this brings wwstage
to cstage's leaner shape, mirroring A.6.3g (8da1414).
Silent-false coverage gain: multi-level chains and tuple-positional
bases (`t.1.field` for a str element) now resolve via the checker
stamp instead of returning false. The "Not covered: tuple-positional"
bullet in nodeisstr's docstring is removed accordingly. dotchainresolve
itself is untouched -- still used by cgenexpr (cgdot read-path, &-of
address-of, structlit BP write-back). Byte-identity (994/995) is the
gate.
make test 133/133 ok. Net cgenutil.ww -110.
This commit is contained in:
@@ -563,74 +563,13 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// N_DOT to a slice field: resolve the field through the struct
|
||||
// (or *struct) the base ident / inner chain lands on, then check
|
||||
// the field tnode. Mirrors nodeisstr's N_DOT branch so call-arg
|
||||
// push/pop counts 3 words for `p.sl` and `p.inner.sl` shapes.
|
||||
// `.ptr` / `.len` / `.cap` are pseudo-fields — they yield ptr
|
||||
// (*u8) and i32, not a slice — so we exclude them up front.
|
||||
// N_DOT: read the checker-stamped n.type_. Struct field, nested
|
||||
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
|
||||
// resolve to the right tinfo via check.ww:1947-1978 (pseudo-field
|
||||
// + struct-field stamps). Cstage cgen.c:182-184 node_isslice =
|
||||
// type_isslice(n->type) — same shape. Collapsed per A.6.3h (#56).
|
||||
if (k == nkind.N_DOT) {
|
||||
let base: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
if (base != nil) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let innert: *node = dotinnerstructptr(c, base);
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) { sname = innert.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)) {
|
||||
return isslicetype(c, fi.tnode);
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`o.inner.sl`,
|
||||
// `p.inner.sl`): dotinnerstructptr above only walks
|
||||
// *struct fields, so a value-struct chain falls through.
|
||||
// dotchainresolve handles arbitrary depth through value
|
||||
// struct AND `*T` root, returning the leaf fieldinfo.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let lfi: *fieldinfo = nil;
|
||||
let sdelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let ptrroot: bool = false;
|
||||
let ok: bool = dotchainresolve(c, n,
|
||||
&rootnm, &rootoff, &totaloff,
|
||||
&lfi, &sdelta, &isglobal, &ptrroot);
|
||||
if (ok && sdelta < 0 && lfi != nil) {
|
||||
return isslicetype(c, lfi.tnode);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
@@ -646,13 +585,11 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
// 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 (struct field / chained / pseudo-fields excluded), N_CAST.
|
||||
// N_DOT (reads checker-stamped 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
|
||||
// help. Tracked alongside the broader cgun-load-shape gap.
|
||||
// - N_DOT to a tuple positional `t.1` of a str element — wwstage's
|
||||
// cgdot loads (AX, BX) but tuple-as-arg has independent issues.
|
||||
fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
let k: nkind = n.kind;
|
||||
@@ -775,73 +712,13 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// N_DOT: read the checker-stamped n.type_. Struct field, nested
|
||||
// dot, value-struct hops, and pseudo-fields (.ptr/.len/.cap) all
|
||||
// resolve to the right tinfo via check.ww:1947-1978. Cstage
|
||||
// cgen.c:168-170 node_isstr = type_isstr(n->type) — same shape.
|
||||
// Collapsed per A.6.3h (#56).
|
||||
if (k == nkind.N_DOT) {
|
||||
let base: *node = n.lhs;
|
||||
let fld: str = n.str;
|
||||
// `<expr>.ptr` is *u8 not str; `<expr>.len` is i32 not str.
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
if (streq(fld, "len")) { return false; };
|
||||
if (streq(fld, "cap")) { return false; };
|
||||
if (base != nil) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
let lkind: nkind = nkind.N_NONE;
|
||||
if (tn != nil) { lkind = tn.kind; };
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let inner: *node = tn.lhs;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot (`p.foo.bar`): use dotinnerstructptr
|
||||
// to resolve the inner chain to the *struct it lands
|
||||
// on, then look up `fld` in that struct.
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let innert: *node = dotinnerstructptr(c, base);
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) { sname = innert.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fld)) {
|
||||
return isstrtype(c, fi.tnode);
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
// Chained dot through value-struct hops (`p.inner.s`):
|
||||
// dotinnerstructptr above only walks *struct fields;
|
||||
// dotchainresolve handles arbitrary depth through
|
||||
// value struct AND `*T` root. Mirror of the nodeisslice
|
||||
// fallback so chained str-field args also push 2 words.
|
||||
let rootnm: str = "";
|
||||
let rootoff: i32 = 0;
|
||||
let totaloff: i32 = 0;
|
||||
let lfi: *fieldinfo = nil;
|
||||
let sdelta: i32 = -1;
|
||||
let isglobal: bool = false;
|
||||
let ptrroot: bool = false;
|
||||
let ok: bool = dotchainresolve(c, n,
|
||||
&rootnm, &rootoff, &totaloff,
|
||||
&lfi, &sdelta, &isglobal, &ptrroot);
|
||||
if (ok && sdelta < 0 && lfi != nil) {
|
||||
return isstrtype(c, lfi.tnode);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
return typeisstr(n.type_: *tinfo);
|
||||
};
|
||||
if (k == nkind.N_CAST) {
|
||||
return isstrtype(c, n.rhs);
|
||||
|
||||
Reference in New Issue
Block a user