diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index b2e28622..cdd1e12d 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -11911,8 +11911,30 @@ export fn exprprimresolved(c: *cgen, n: *node, return; }; if (k == nkind.N_DOT) { - typenodeprimresolved(c, dotfieldtnode(c, n), - sz_out, unsigned_out); + // #59: read the checker-stamped tinfo instead of re-deriving the + // field type via dotfieldtnode's structinfo walk. Mirrors cstage + // castsrcprim N_DOT (cmd/w6c/cgen.c:323-344): the base must + // resolve to a struct (or ptr-to-struct) before the field type + // counts. That guard excludes pseudo-fields .len/.cap/.ptr (the + // checker stamps them i32/*T at check.ww:1987-2004) and tuple + // positionals, keeping them at sz=0 — asymmetry there breaks 995 + // byte-id (cgen.c:286-290). The field's own width/sign is the + // N_DOT's stamped type_ (check.ww:2012). One TY_NAMED peel, then + // typeisint ? size : 0; bool falls out because typeisint(bool) is + // false — the same exclusion the old streq("bool") arm encoded. + let bu: *tinfo = nil; + if (n.lhs != nil) { bu = n.lhs.type_: *tinfo; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_STRUCT) { + let u: *tinfo = n.type_: *tinfo; + if (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; }; + if (typeisint(u)) { + *sz_out = u.size: i32; + *unsigned_out = typeisunsigned(u); + }; + }; return; }; }; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index debc152d..56e5ef63 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1470,8 +1470,30 @@ export fn exprprimresolved(c: *cgen, n: *node, return; }; if (k == nkind.N_DOT) { - typenodeprimresolved(c, dotfieldtnode(c, n), - sz_out, unsigned_out); + // #59: read the checker-stamped tinfo instead of re-deriving the + // field type via dotfieldtnode's structinfo walk. Mirrors cstage + // castsrcprim N_DOT (cmd/w6c/cgen.c:323-344): the base must + // resolve to a struct (or ptr-to-struct) before the field type + // counts. That guard excludes pseudo-fields .len/.cap/.ptr (the + // checker stamps them i32/*T at check.ww:1987-2004) and tuple + // positionals, keeping them at sz=0 — asymmetry there breaks 995 + // byte-id (cgen.c:286-290). The field's own width/sign is the + // N_DOT's stamped type_ (check.ww:2012). One TY_NAMED peel, then + // typeisint ? size : 0; bool falls out because typeisint(bool) is + // false — the same exclusion the old streq("bool") arm encoded. + let bu: *tinfo = nil; + if (n.lhs != nil) { bu = n.lhs.type_: *tinfo; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_STRUCT) { + let u: *tinfo = n.type_: *tinfo; + if (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; }; + if (typeisint(u)) { + *sz_out = u.size: i32; + *unsigned_out = typeisunsigned(u); + }; + }; return; }; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index f433b852..44829cf5 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -11911,8 +11911,30 @@ export fn exprprimresolved(c: *cgen, n: *node, return; }; if (k == nkind.N_DOT) { - typenodeprimresolved(c, dotfieldtnode(c, n), - sz_out, unsigned_out); + // #59: read the checker-stamped tinfo instead of re-deriving the + // field type via dotfieldtnode's structinfo walk. Mirrors cstage + // castsrcprim N_DOT (cmd/w6c/cgen.c:323-344): the base must + // resolve to a struct (or ptr-to-struct) before the field type + // counts. That guard excludes pseudo-fields .len/.cap/.ptr (the + // checker stamps them i32/*T at check.ww:1987-2004) and tuple + // positionals, keeping them at sz=0 — asymmetry there breaks 995 + // byte-id (cgen.c:286-290). The field's own width/sign is the + // N_DOT's stamped type_ (check.ww:2012). One TY_NAMED peel, then + // typeisint ? size : 0; bool falls out because typeisint(bool) is + // false — the same exclusion the old streq("bool") arm encoded. + let bu: *tinfo = nil; + if (n.lhs != nil) { bu = n.lhs.type_: *tinfo; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; + if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + if (bu != nil && bu.kind == tykind.TY_STRUCT) { + let u: *tinfo = n.type_: *tinfo; + if (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; }; + if (typeisint(u)) { + *sz_out = u.size: i32; + *unsigned_out = typeisunsigned(u); + }; + }; return; }; };