diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 4e3eb7ef..15973d00 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -10947,74 +10947,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; }; @@ -11030,13 +10969,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; @@ -11159,73 +11096,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; - // `.ptr` is *u8 not str; `.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); diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index d63ae2db..1e0f7e75 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -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; - // `.ptr` is *u8 not str; `.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); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 25c2aed3..89b2b74e 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -10947,74 +10947,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; }; @@ -11030,13 +10969,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; @@ -11159,73 +11096,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; - // `.ptr` is *u8 not str; `.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);