From 8da14147b395cdfb120cf7c20ef66ed854d8021c Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 23 May 2026 13:59:06 +0900 Subject: [PATCH] selfhost/cmd/wcc/cgenutil: collapse N_DOT arms onto n.type_ (#55, A.6.3g) nodeisunsigned + exprfloatkind each manually walked base->struct->field on N_DOT, gated on `base.kind == nkind.N_IDENT` -- duplicating cstage's behaviour at the AST level while silently dropping the nested-N_DOT case (`a.b.c` returned the conservative default). After A.6.2 the checker stamps n.type_ on every N_DOT expression (check.ww:1973 struct-field arm of exprtype), and A.6.3a/b landed the tinfo helpers (typeisunsigned, isfloattype/isf32type) that the inner reads already use. Both arms collapse to one tinfo read. Polarity DOWN per rule 10: cstage was already aligned. cmd/w6c/cgen.c: 2330-2331 reads type_isunsigned(n->lhs->type) directly; cgen.c:152-156 node_isfloat = cg_isfloat(n->type); cgen.c:195-199 node_isf32 = type_isf32(n->type). No exprfloatkind-equivalent walker exists in cstage -- it is pre-A.6.2 wwstage scaffolding. Wwstage now reads the same shape as cstage on N_DOT. Nested N_DOT (a.b.c) now resolves to the field type instead of returning the conservative default. Byte-identity (994/995) confirms the codegen matches cstage on the test set -- cstage was already getting nested-dot right via checker-stamped n->type, wwstage was the laggard. make test 133/133 ok. Net cgenutil.ww -41 / +3. --- selfhost/cmd/w6c/main.combined.ww | 84 +++------------------------- selfhost/cmd/wcc/cgenutil.ww | 84 +++------------------------- selfhost/cmd/wwdump/main.combined.ww | 84 +++------------------------- 3 files changed, 27 insertions(+), 225 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index dfa07613..4e3eb7ef 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -11587,10 +11587,10 @@ fn indexvaluetnode(c: *cgen, n: *node) *node = { }; // nodeisunsigned — best-effort cgen-time inference from the AST. We -// don't have a typed AST yet, so we walk surface nodes: +// walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) // nkind.N_IDENT — look up the local's declared type -// nkind.N_DOT — look up the field's declared type via struct reg +// nkind.N_DOT — read the checker-stamped n.type_ (#55 A.6.3g) // nkind.N_BIN / nkind.N_UN — recurse: unsigned if either operand is unsigned // nkind.N_CAST — use the cast target type // @@ -11609,43 +11609,7 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = { return false; }; if (k == nkind.N_DOT) { - let base: *node = n.lhs; - let fld: str = n.str; - if (base != nil) { - if (base.kind == nkind.N_IDENT) { - let bn: str = base.str; - let lc: *local = localfindnode(c, bn); - if (lc != nil) { - let tn: *node = lc.tnode; - let lkind: nkind = nkind.N_NONE; - if (tn != nil) { lkind = tn.kind; }; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (lkind == nkind.N_TPTR) { - let inner: *node = tn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (lkind == nkind.N_TNAME) { sname = tn.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)) { - if (fi.tnode == nil) { return false; }; - return typeisunsigned(fi.tnode.type_: *tinfo); - }; - fi = fi.finext; - }; - }; - }; - }; - }; - }; - return false; + return typeisunsigned(n.type_: *tinfo); }; if (k == nkind.N_CAST) { if (n.rhs == nil) { return false; }; @@ -12537,42 +12501,12 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = { // `p.field` where the struct field is f64/f32. Without this, // `v.fval: i64` lowers to CVTSI on an integer-load value // instead of CVTTSD2SI on the X0 the cgdot path actually - // emits for an f64 field. - let base: *node = n.lhs; - let fld: str = n.str; - 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; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { sname = tn.str; }; - if (tn.kind == nkind.N_TPTR) { - let pe: *node = tn.lhs; - if (pe != nil) { - if (pe.kind == nkind.N_TNAME) { sname = pe.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)) { - if (isf32type(c, fi.tnode)) { return 1; }; - if (isfloattype(c, fi.tnode)) { return 2; }; - return 0; - }; - fi = fi.finext; - }; - }; - }; - }; + // emits for an f64 field. Read the checker-stamped tinfo on + // the N_DOT itself (check.ww:1973 stamps the field type); + // cstage cgen.c:2128 reads node_isfloat(n) the same way. + // Collapsed per A.6.3g (#55). + if (isf32type(c, n)) { return 1; }; + if (isfloattype(c, n)) { return 2; }; return 0; }; return 0; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 49e4b60b..d63ae2db 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1203,10 +1203,10 @@ fn indexvaluetnode(c: *cgen, n: *node) *node = { }; // nodeisunsigned — best-effort cgen-time inference from the AST. We -// don't have a typed AST yet, so we walk surface nodes: +// walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) // nkind.N_IDENT — look up the local's declared type -// nkind.N_DOT — look up the field's declared type via struct reg +// nkind.N_DOT — read the checker-stamped n.type_ (#55 A.6.3g) // nkind.N_BIN / nkind.N_UN — recurse: unsigned if either operand is unsigned // nkind.N_CAST — use the cast target type // @@ -1225,43 +1225,7 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = { return false; }; if (k == nkind.N_DOT) { - let base: *node = n.lhs; - let fld: str = n.str; - if (base != nil) { - if (base.kind == nkind.N_IDENT) { - let bn: str = base.str; - let lc: *local = localfindnode(c, bn); - if (lc != nil) { - let tn: *node = lc.tnode; - let lkind: nkind = nkind.N_NONE; - if (tn != nil) { lkind = tn.kind; }; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (lkind == nkind.N_TPTR) { - let inner: *node = tn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (lkind == nkind.N_TNAME) { sname = tn.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)) { - if (fi.tnode == nil) { return false; }; - return typeisunsigned(fi.tnode.type_: *tinfo); - }; - fi = fi.finext; - }; - }; - }; - }; - }; - }; - return false; + return typeisunsigned(n.type_: *tinfo); }; if (k == nkind.N_CAST) { if (n.rhs == nil) { return false; }; @@ -2153,42 +2117,12 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = { // `p.field` where the struct field is f64/f32. Without this, // `v.fval: i64` lowers to CVTSI on an integer-load value // instead of CVTTSD2SI on the X0 the cgdot path actually - // emits for an f64 field. - let base: *node = n.lhs; - let fld: str = n.str; - 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; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { sname = tn.str; }; - if (tn.kind == nkind.N_TPTR) { - let pe: *node = tn.lhs; - if (pe != nil) { - if (pe.kind == nkind.N_TNAME) { sname = pe.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)) { - if (isf32type(c, fi.tnode)) { return 1; }; - if (isfloattype(c, fi.tnode)) { return 2; }; - return 0; - }; - fi = fi.finext; - }; - }; - }; - }; + // emits for an f64 field. Read the checker-stamped tinfo on + // the N_DOT itself (check.ww:1973 stamps the field type); + // cstage cgen.c:2128 reads node_isfloat(n) the same way. + // Collapsed per A.6.3g (#55). + if (isf32type(c, n)) { return 1; }; + if (isfloattype(c, n)) { return 2; }; return 0; }; return 0; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 8ca8484f..25c2aed3 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -11587,10 +11587,10 @@ fn indexvaluetnode(c: *cgen, n: *node) *node = { }; // nodeisunsigned — best-effort cgen-time inference from the AST. We -// don't have a typed AST yet, so we walk surface nodes: +// walk surface nodes (N_DOT now reads n.type_ — #55 A.6.3g): // nkind.N_INTLIT — never marked unsigned (no tsuffix plumbing yet) // nkind.N_IDENT — look up the local's declared type -// nkind.N_DOT — look up the field's declared type via struct reg +// nkind.N_DOT — read the checker-stamped n.type_ (#55 A.6.3g) // nkind.N_BIN / nkind.N_UN — recurse: unsigned if either operand is unsigned // nkind.N_CAST — use the cast target type // @@ -11609,43 +11609,7 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = { return false; }; if (k == nkind.N_DOT) { - let base: *node = n.lhs; - let fld: str = n.str; - if (base != nil) { - if (base.kind == nkind.N_IDENT) { - let bn: str = base.str; - let lc: *local = localfindnode(c, bn); - if (lc != nil) { - let tn: *node = lc.tnode; - let lkind: nkind = nkind.N_NONE; - if (tn != nil) { lkind = tn.kind; }; - let sname: str; - sname.ptr = nil; sname.len = 0; - if (lkind == nkind.N_TPTR) { - let inner: *node = tn.lhs; - if (inner != nil) { - if (inner.kind == nkind.N_TNAME) { sname = inner.str; }; - }; - }; - if (lkind == nkind.N_TNAME) { sname = tn.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)) { - if (fi.tnode == nil) { return false; }; - return typeisunsigned(fi.tnode.type_: *tinfo); - }; - fi = fi.finext; - }; - }; - }; - }; - }; - }; - return false; + return typeisunsigned(n.type_: *tinfo); }; if (k == nkind.N_CAST) { if (n.rhs == nil) { return false; }; @@ -12537,42 +12501,12 @@ export fn exprfloatkind(c: *cgen, n: *node) i32 = { // `p.field` where the struct field is f64/f32. Without this, // `v.fval: i64` lowers to CVTSI on an integer-load value // instead of CVTTSD2SI on the X0 the cgdot path actually - // emits for an f64 field. - let base: *node = n.lhs; - let fld: str = n.str; - 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; - if (tn != nil) { - if (tn.kind == nkind.N_TNAME) { sname = tn.str; }; - if (tn.kind == nkind.N_TPTR) { - let pe: *node = tn.lhs; - if (pe != nil) { - if (pe.kind == nkind.N_TNAME) { sname = pe.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)) { - if (isf32type(c, fi.tnode)) { return 1; }; - if (isfloattype(c, fi.tnode)) { return 2; }; - return 0; - }; - fi = fi.finext; - }; - }; - }; - }; + // emits for an f64 field. Read the checker-stamped tinfo on + // the N_DOT itself (check.ww:1973 stamps the field type); + // cstage cgen.c:2128 reads node_isfloat(n) the same way. + // Collapsed per A.6.3g (#55). + if (isf32type(c, n)) { return 1; }; + if (isfloattype(c, n)) { return 2; }; return 0; }; return 0;