diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 125a0d99..60d7360f 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -17990,7 +17990,7 @@ export fn sretretsize(c: *cgen, t: *node) i32 = { }; if (r.kind != nkind.N_TNAME) { return 0; }; // Primitives / aliased-to-primitives are never sret. - if (primsize(r.str) > 0) { return 0; }; + if (aliasprimsize(c, r.str) > 0) { return 0; }; if (streq(r.str, "str")) { return 0; }; let si: *structinfo = structlookup(c, r.str); if (si == nil) { @@ -18121,6 +18121,9 @@ fn fldnumidx(s: str) i32 = { return r; }; +// primsize-ok (#101/#109): the primitive-width oracle itself — this +// IS the SSoT table aliasprimsize wraps; there is nothing below it to +// chase. fn primsize(name: str) i32 = { if (streq(name, "u8")) { return 1; }; if (streq(name, "i8")) { return 1; }; @@ -18154,6 +18157,8 @@ fn primsize(name: str) i32 = { // at the twin sites; this is the ww align-up. The bare-primsize GUARD // family (is-primitive dispatch) is the #109 follow-on, NOT routed // here. #101. +// primsize-ok (#101/#109): the SSoT chase body itself — primsize is +// the leaf-primitive probe this helper wraps, then aliaslookup chases. fn aliasprimsize(c: *cgen, nm: str) i32 = { let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; @@ -18195,6 +18200,9 @@ export fn typenodeprimresolved(c: *cgen, t: *node, // dedicated `is_bool` path in cgcast owns bool→bool's // ANDQ $255 on both stages. if (streq(nm, "bool")) { return; }; + // primsize-ok (#101/#109): this fn IS a prim-resolver + // chaser (#33) — primsize is the leaf-primitive probe; + // aliaslookup below advances the walk on a miss. let ps: i32 = primsize(nm); if (ps > 0) { *sz_out = ps; @@ -18232,6 +18240,9 @@ export fn exprprimresolved(c: *cgen, n: *node, // mymode;` (mymode = enum u32). let s: str = n.tsuffix; if (s.len > 0) { + // primsize-ok (#101/#109): a typed-int literal suffix + // (`7u32`) is a builtin primitive name by grammar — no + // alias can reach here, so there is nothing to chase. let ps: i32 = primsize(s); if (ps > 0) { *sz_out = ps; @@ -18707,7 +18718,7 @@ fn structparamsize(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -18796,7 +18807,7 @@ fn structfloatclass(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -20742,7 +20753,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let isi: *structinfo = structlookup(c, fi.tnode.str); if (isi != nil) { cgstructlitfill(c, isi, @@ -20788,7 +20799,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_CALL) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let csi: *structinfo = structlookup(c, fi.tnode.str); if (csi != nil) { // Inner struct's ABI size @@ -22043,6 +22054,9 @@ fn cgcast(c: *cgen, n: *node) void = { else { if (lk == nkind.N_TENUM) { leaf_tn = leaf_tn.lhs; } else { if (lk == nkind.N_TNAME) { let lnm: str = leaf_tn.str; + // primsize-ok (#101/#109): this IS an alias chase loop + // — primsize is the leaf-primitive break test the loop + // wraps (aliaslookup advances the cursor on a miss). if (primsize(lnm) > 0) { break; }; let lal: *node = aliaslookup(c, lnm); if (lal == nil) { leaf_tn = nil; } @@ -25309,7 +25323,7 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; @@ -28830,6 +28844,9 @@ fn cgassign(c: *cgen, n: *node) void = { else { if (streq(pe.str, "f64")) { elemfloat = true; } else { if (streq(pe.str, "f32")) { elemfloat = true; elemf32 = true; } else { + // primsize-ok (#101/#109): this site OWNS its own ps==0 + // typenodeprimresolved chase below — routing through + // aliasprimsize would double-resolve and regress #11. let ps: i32 = primsize(pe.str); // #11: primsize is name-keyed and // misses a `!`/enum/name alias @@ -30047,7 +30064,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30102,7 +30119,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 1, lc.off, "", @@ -30115,7 +30132,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -30281,7 +30298,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30332,7 +30349,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 0, 0, "", @@ -30345,7 +30362,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -30570,7 +30587,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30625,7 +30642,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 2, 0, bn, @@ -30638,7 +30655,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -31417,7 +31434,7 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 173b47c7..e481fb11 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -867,6 +867,9 @@ fn cgcast(c: *cgen, n: *node) void = { else { if (lk == nkind.N_TENUM) { leaf_tn = leaf_tn.lhs; } else { if (lk == nkind.N_TNAME) { let lnm: str = leaf_tn.str; + // primsize-ok (#101/#109): this IS an alias chase loop + // — primsize is the leaf-primitive break test the loop + // wraps (aliaslookup advances the cursor on a miss). if (primsize(lnm) > 0) { break; }; let lal: *node = aliaslookup(c, lnm); if (lal == nil) { leaf_tn = nil; } @@ -4133,7 +4136,7 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; @@ -7654,6 +7657,9 @@ fn cgassign(c: *cgen, n: *node) void = { else { if (streq(pe.str, "f64")) { elemfloat = true; } else { if (streq(pe.str, "f32")) { elemfloat = true; elemf32 = true; } else { + // primsize-ok (#101/#109): this site OWNS its own ps==0 + // typenodeprimresolved chase below — routing through + // aliasprimsize would double-resolve and regress #11. let ps: i32 = primsize(pe.str); // #11: primsize is name-keyed and // misses a `!`/enum/name alias @@ -8871,7 +8877,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -8926,7 +8932,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 1, lc.off, "", @@ -8939,7 +8945,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -9105,7 +9111,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -9156,7 +9162,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 0, 0, "", @@ -9169,7 +9175,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -9394,7 +9400,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -9449,7 +9455,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 2, 0, bn, @@ -9462,7 +9468,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -10241,7 +10247,7 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index c3c9181b..3cf4b3be 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1849,7 +1849,7 @@ export fn sretretsize(c: *cgen, t: *node) i32 = { }; if (r.kind != nkind.N_TNAME) { return 0; }; // Primitives / aliased-to-primitives are never sret. - if (primsize(r.str) > 0) { return 0; }; + if (aliasprimsize(c, r.str) > 0) { return 0; }; if (streq(r.str, "str")) { return 0; }; let si: *structinfo = structlookup(c, r.str); if (si == nil) { @@ -1980,6 +1980,9 @@ fn fldnumidx(s: str) i32 = { return r; }; +// primsize-ok (#101/#109): the primitive-width oracle itself — this +// IS the SSoT table aliasprimsize wraps; there is nothing below it to +// chase. fn primsize(name: str) i32 = { if (streq(name, "u8")) { return 1; }; if (streq(name, "i8")) { return 1; }; @@ -2013,6 +2016,8 @@ fn primsize(name: str) i32 = { // at the twin sites; this is the ww align-up. The bare-primsize GUARD // family (is-primitive dispatch) is the #109 follow-on, NOT routed // here. #101. +// primsize-ok (#101/#109): the SSoT chase body itself — primsize is +// the leaf-primitive probe this helper wraps, then aliaslookup chases. fn aliasprimsize(c: *cgen, nm: str) i32 = { let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; @@ -2054,6 +2059,9 @@ export fn typenodeprimresolved(c: *cgen, t: *node, // dedicated `is_bool` path in cgcast owns bool→bool's // ANDQ $255 on both stages. if (streq(nm, "bool")) { return; }; + // primsize-ok (#101/#109): this fn IS a prim-resolver + // chaser (#33) — primsize is the leaf-primitive probe; + // aliaslookup below advances the walk on a miss. let ps: i32 = primsize(nm); if (ps > 0) { *sz_out = ps; @@ -2091,6 +2099,9 @@ export fn exprprimresolved(c: *cgen, n: *node, // mymode;` (mymode = enum u32). let s: str = n.tsuffix; if (s.len > 0) { + // primsize-ok (#101/#109): a typed-int literal suffix + // (`7u32`) is a builtin primitive name by grammar — no + // alias can reach here, so there is nothing to chase. let ps: i32 = primsize(s); if (ps > 0) { *sz_out = ps; @@ -2566,7 +2577,7 @@ fn structparamsize(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -2655,7 +2666,7 @@ fn structfloatclass(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -4601,7 +4612,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let isi: *structinfo = structlookup(c, fi.tnode.str); if (isi != nil) { cgstructlitfill(c, isi, @@ -4647,7 +4658,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_CALL) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let csi: *structinfo = structlookup(c, fi.tnode.str); if (csi != nil) { // Inner struct's ABI size diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 45bee84c..3a9b5d92 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -17990,7 +17990,7 @@ export fn sretretsize(c: *cgen, t: *node) i32 = { }; if (r.kind != nkind.N_TNAME) { return 0; }; // Primitives / aliased-to-primitives are never sret. - if (primsize(r.str) > 0) { return 0; }; + if (aliasprimsize(c, r.str) > 0) { return 0; }; if (streq(r.str, "str")) { return 0; }; let si: *structinfo = structlookup(c, r.str); if (si == nil) { @@ -18121,6 +18121,9 @@ fn fldnumidx(s: str) i32 = { return r; }; +// primsize-ok (#101/#109): the primitive-width oracle itself — this +// IS the SSoT table aliasprimsize wraps; there is nothing below it to +// chase. fn primsize(name: str) i32 = { if (streq(name, "u8")) { return 1; }; if (streq(name, "i8")) { return 1; }; @@ -18154,6 +18157,8 @@ fn primsize(name: str) i32 = { // at the twin sites; this is the ww align-up. The bare-primsize GUARD // family (is-primitive dispatch) is the #109 follow-on, NOT routed // here. #101. +// primsize-ok (#101/#109): the SSoT chase body itself — primsize is +// the leaf-primitive probe this helper wraps, then aliaslookup chases. fn aliasprimsize(c: *cgen, nm: str) i32 = { let ps: i32 = primsize(nm); if (ps > 0) { return ps; }; @@ -18195,6 +18200,9 @@ export fn typenodeprimresolved(c: *cgen, t: *node, // dedicated `is_bool` path in cgcast owns bool→bool's // ANDQ $255 on both stages. if (streq(nm, "bool")) { return; }; + // primsize-ok (#101/#109): this fn IS a prim-resolver + // chaser (#33) — primsize is the leaf-primitive probe; + // aliaslookup below advances the walk on a miss. let ps: i32 = primsize(nm); if (ps > 0) { *sz_out = ps; @@ -18232,6 +18240,9 @@ export fn exprprimresolved(c: *cgen, n: *node, // mymode;` (mymode = enum u32). let s: str = n.tsuffix; if (s.len > 0) { + // primsize-ok (#101/#109): a typed-int literal suffix + // (`7u32`) is a builtin primitive name by grammar — no + // alias can reach here, so there is nothing to chase. let ps: i32 = primsize(s); if (ps > 0) { *sz_out = ps; @@ -18707,7 +18718,7 @@ fn structparamsize(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -18796,7 +18807,7 @@ fn structfloatclass(c: *cgen, t: *node) i32 = { if (r.kind != nkind.N_TNAME) { return 0; }; let nm: str = r.str; if (streq(nm, "str")) { return 0; }; - if (primsize(nm) > 0) { return 0; }; + if (aliasprimsize(c, nm) > 0) { return 0; }; let si: *structinfo = structlookup(c, nm); if (si == nil) { return 0; }; if (si.totsize <= 0) { return 0; }; @@ -20742,7 +20753,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_STRUCTLIT) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let isi: *structinfo = structlookup(c, fi.tnode.str); if (isi != nil) { cgstructlitfill(c, isi, @@ -20788,7 +20799,7 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *node, if (fieldnode.lhs.kind == nkind.N_CALL) { if (fi.tnode != nil) { if (fi.tnode.kind == nkind.N_TNAME) { - if (primsize(fi.tnode.str) == 0) { + if (aliasprimsize(c, fi.tnode.str) == 0) { let csi: *structinfo = structlookup(c, fi.tnode.str); if (csi != nil) { // Inner struct's ABI size @@ -22043,6 +22054,9 @@ fn cgcast(c: *cgen, n: *node) void = { else { if (lk == nkind.N_TENUM) { leaf_tn = leaf_tn.lhs; } else { if (lk == nkind.N_TNAME) { let lnm: str = leaf_tn.str; + // primsize-ok (#101/#109): this IS an alias chase loop + // — primsize is the leaf-primitive break test the loop + // wraps (aliaslookup advances the cursor on a miss). if (primsize(lnm) > 0) { break; }; let lal: *node = aliaslookup(c, lnm); if (lal == nil) { leaf_tn = nil; } @@ -25309,7 +25323,7 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; @@ -28830,6 +28844,9 @@ fn cgassign(c: *cgen, n: *node) void = { else { if (streq(pe.str, "f64")) { elemfloat = true; } else { if (streq(pe.str, "f32")) { elemfloat = true; elemf32 = true; } else { + // primsize-ok (#101/#109): this site OWNS its own ps==0 + // typenodeprimresolved chase below — routing through + // aliasprimsize would double-resolve and regress #11. let ps: i32 = primsize(pe.str); // #11: primsize is name-keyed and // misses a `!`/enum/name alias @@ -30047,7 +30064,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30102,7 +30119,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 1, lc.off, "", @@ -30115,7 +30132,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -30281,7 +30298,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30332,7 +30349,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 0, 0, "", @@ -30345,7 +30362,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -30570,7 +30587,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_CALL && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { let ssz: i32 = structabisize(ssi); @@ -30625,7 +30642,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_STRUCTLIT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); if (ssi != nil) { cgstructlitfill(c, ssi, n.rhs, 2, 0, bn, @@ -30638,7 +30655,7 @@ fn cgassign(c: *cgen, n: *node) void = { && n.rhs.kind == nkind.N_IDENT && fi.tnode != nil && fi.tnode.kind == nkind.N_TNAME - && primsize(fi.tnode.str) == 0) { + && aliasprimsize(c, fi.tnode.str) == 0) { let ssi: *structinfo = structlookup(c, fi.tnode.str); let srhs: *local = localfindnode(c, n.rhs.str); if (ssi != nil) { if (srhs != nil) { @@ -31417,7 +31434,7 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(ofi.fname, innerfld)) { let oft: *node = ofi.tnode; if (oft != nil) { if (oft.kind == nkind.N_TNAME) { - if (primsize(oft.str) == 0) { + if (aliasprimsize(c, oft.str) == 0) { let isi: *structinfo = structlookup(c, oft.str); if (isi != nil) { let ffi: *fieldinfo = isi.fields; diff --git a/test/wcc/944_peellint_gate.c b/test/wcc/944_peellint_gate.c index 88a6a01a..25d47d98 100644 --- a/test/wcc/944_peellint_gate.c +++ b/test/wcc/944_peellint_gate.c @@ -22,6 +22,18 @@ * `(*t).under`, ww `t. under`, and a string literal containing * a block-comment OPENER token that blinded the old regex * comment-strip for the rest of the file. + * 6. RULE 2 (#101/#109) — bare primsize() in the ww stage is the + * alias-blind width shape aliasprimsize() supersedes. A bare + * `primsize(` REDS; the SSoT wrapper `aliasprimsize(` must NOT + * (left word boundary); the evasion spellings (space-before-paren, + * name-at-EOL line split, string-blind block-comment opener in a + * literal, paren-wrap `(primsize)(nm)`, function-value bind + * `let p = primsize` — the last two reviewer-109-found, both + * compile + run) all RED; a + * `primsize-ok` annotation exempts; a corrupted one does not; the + * primsize-ok and peel-ok windows are independent (neither blinds + * the other's shape); and a C-file `primsize(` is out of scope + * (the C stage chases via type_chase_named, no primsize symbol). * * Scratch trees live under /tmp and exercise the lint via its ROOT * override (sizelint-style), so the real tree is never touched. @@ -159,6 +171,79 @@ static const struct lintrow lintrows[] = { { "evade_c_string_blind", "cmd/w6c/x.c", "static const char *s = \"/*\";\n" "static Type *f(Type *t) { return t->under; }\n", 1 }, + /* RULE 2 (#101/#109): bare primsize() outside the chase is the + * forbidden alias-blind width shape; aliasprimsize is the SSoT. */ + { "prim_bare", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet z: i32 = primsize(nm);\n" + "\treturn z;\n" + "};\n", 1 }, + { "prim_evade_spacing", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet z: i32 = primsize (nm);\n" + "\treturn z;\n" + "};\n", 1 }, + { "prim_evade_linesplit", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet z: i32 = primsize\n" + "\t (nm);\n" + "\treturn z;\n" + "};\n", 1 }, + { "prim_evade_string_blind", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen) str = {\n" + "\tlet s: str = \"/*\";\n" + "\tlet z: i32 = primsize(s);\n" + "\treturn s;\n" + "};\n", 1 }, + /* review-109 evasions: both COMPILE + run (verified) yet slipped a + * `primsize(`-only matcher — the token rule reds them. */ + { "prim_evade_parenwrap", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet z: i32 = (primsize)(nm);\n" + "\treturn z;\n" + "};\n", 1 }, + { "prim_evade_fnvalue", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet p = primsize;\n" + "\treturn p(nm);\n" + "};\n", 1 }, + /* aliasprimsize() is the SSoT wrapper — its `primsize` suffix must + * NOT trip the left-word-bounded matcher (the central evasion). */ + { "prim_alias_wrapper_ok", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\tlet z: i32 = aliasprimsize(c, nm);\n" + "\treturn z;\n" + "};\n", 0 }, + { "prim_annotated_ok", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\t// primsize-ok: chase body\n" + "\tlet z: i32 = primsize(nm);\n" + "\treturn z;\n" + "};\n", 0 }, + { "prim_corrupt_annotation", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\t// primsize-okk-corrupt: nope\n" + "\tlet z: i32 = primsize(nm);\n" + "\treturn z;\n" + "};\n", 1 }, + /* the two exemption windows are independent: primsize-ok must not + * blind an under-token peel, nor peel-ok a bare primsize. */ + { "prim_window_no_cross_under", "selfhost/cmd/wcc/x.ww", + "fn f(t: *tinfo) *tinfo = {\n" + "\t// primsize-ok: must NOT exempt the under peel below\n" + "\treturn t.under;\n" + "};\n", 1 }, + { "peel_window_no_cross_prim", "selfhost/cmd/wcc/x.ww", + "fn f(c: *cgen, nm: str) i32 = {\n" + "\t// peel-ok: must NOT exempt the primsize below\n" + "\tlet z: i32 = primsize(nm);\n" + "\treturn z;\n" + "};\n", 1 }, + /* RULE 2 is ww-only: the C stage dealiases via type_chase_named and + * has no primsize symbol — a C `primsize(` is not in scope. */ + { "prim_c_file_out_of_scope", "cmd/w6c/x.c", + "static int primsize(const char *n) { return 0; }\n" + "int g(void) { return primsize(\"u8\"); }\n", 0 }, }; int diff --git a/tools/peellint b/tools/peellint index 14fbc1e8..d7c575c6 100755 --- a/tools/peellint +++ b/tools/peellint @@ -1,38 +1,66 @@ #!/bin/sh -# tools/peellint — gate against raw TY_NAMED single-peel reads outside -# the chase accessors. The #5 alias-arc close-by-construction contract -# (rob F2/B7 rulings): one chased accessor is the only spelled way to -# dealias — type_chase_named (cmd/wcc/type.c) on the C side, tichase -# (selfhost/cmd/wcc/cgenutil.ww) on the ww side. A raw `->under` / -# `.under` read peels exactly one NAMED layer; chain-of-aliases stacks -# two, so every kind-gated consumer downstream of a single peel falls -# to a scalar shape (the four-times-burned family: #60/#61/#62/#70…). -# ZERO raw under-token reads may exist in scope outside the annotated -# whitelist; this gate lands in the same commit that deletes the last -# raw read and keeps the class unwritable. +# tools/peellint — gate against two raw single-resolve shapes that +# bypass the alias-chase accessors. # -# What this does NOT close (stated honestly, per the f2-ruling): a +# RULE 1 (raw TY_NAMED single-peel reads) — the #5 alias-arc +# close-by-construction contract (rob F2/B7 rulings): one chased +# accessor is the only spelled way to dealias — type_chase_named +# (cmd/wcc/type.c) on the C side, tichase (selfhost/cmd/wcc/cgenutil.ww) +# on the ww side. A raw `->under` / `.under` read peels exactly one +# NAMED layer; chain-of-aliases stacks two, so every kind-gated consumer +# downstream of a single peel falls to a scalar shape (the four-times- +# burned family: #60/#61/#62/#70…). ZERO raw under-token reads may +# exist in scope outside the annotated whitelist. +# +# RULE 2 (bare primsize() name-keyed width) — the #101/#109 close-by- +# construction contract. primsize(name) is the ww-stage primitive-width +# table; it is ALIAS-BLIND — a narrow alias (`type my32 = u32`) returns +# 0, defaulting strides/widths to 8 (the #101 struct-fill miscompile and +# the #109 is-primitive GUARD family). aliasprimsize(c, name) is the +# SSoT chase (primsize else aliaslookup-chase, cgenutil.ww). A bare +# primsize() outside the chase machinery is the forbidden shape: the +# size/guard sites route through aliasprimsize so an alias resolves. +# This rule is ww-only — the C side dealiases via type_chase_named and +# has no primsize symbol. ZERO bare primsize() calls may exist in the +# ww stage outside the annotated whitelist. +# +# Both rules land in the same commit that deletes the last raw shape and +# keep the class unwritable. +# +# What rule 1 does NOT close (stated honestly, per the f2-ruling): a # consumer that never spells `under` at all — a switch on t->kind that # simply never peels — has no token to see here. That NO-PEEL class is # closed only where classification routes through the internalized # chasing helpers, and contained elsewhere by the acceptance-commit- -# carries-tripwires doctrine. +# carries-tripwires doctrine. Rule 2 has the symmetric edge: a size +# computed by a hardcoded literal instead of primsize is caught by +# sizelint (rule 13), not here. # # Matcher: a character scan strips block/line comments and string/char # literals first (a regex pass mis-nests `/*` inside a string — review -# probe E6), then the under-token is matched accessor-spelling-wide: -# `->under`/`.under` in C (deref-dot `(*t).under` is the same peel), -# `.under` in ww, with optional whitespace after the operator and a -# line-split continuation (`t->` at EOL, `under` next line) — all the -# compiling spellings the B7 review probes evaded with. Right token -# bound keeps prose like "io.underread" (check.ww) out. +# probe E6), then the tokens are matched accessor-spelling-wide: +# rule 1: `->under`/`.under` in C (deref-dot `(*t).under` is the same +# peel), `.under` in ww, with optional whitespace after the operator +# and a line-split continuation (`t->` at EOL, `under` next line). +# rule 2 (ww only): the bare `primsize` token, LEFT+RIGHT word-bounded +# so the superstring `aliasprimsize` is NOT a hit. Matching the +# standalone token (not just `primsize(`) closes the call form +# `primsize(nm)`, the paren-wrap `(primsize)(nm)`, the function-value +# bind `let p = primsize`, and every line-split — all of which +# compile and reintroduce the alias-blind width (review probes). +# Right/left token bounds keep prose like "io.underread" (check.ww) and +# "aliasprimsize" out. # # Exemption: a line containing `peel-ok` (or the equivalent landed -# spelling `peellint-ok`) exempts itself and the following 9 lines — -# wide enough that one annotation atop a short construction/chase body -# covers it, narrow enough that a stray peel can't hide behind a -# distant annotation. Reasons stay WHY-only (rule 8): construction, -# chase body, recursive chase, resolve-state probe, or a cited task. +# spelling `peellint-ok`) exempts rule-1 violations on itself and the +# following 9 lines; a line containing `primsize-ok` exempts rule-2 +# violations over the same window. The windows are kept separate so a +# rule-1 annotation cannot blind a rule-2 bug and vice versa. Wide +# enough that one annotation atop a short construction/chase body covers +# it, narrow enough that a stray shape can't hide behind a distant +# annotation. Reasons stay WHY-only (rule 8): construction, chase body, +# recursive chase, resolve-state probe, structural-by-design sizer, or a +# cited task. # # Scope: cmd/wcc cmd/w6c selfhost/cmd/wcc lib/ww (skip *.combined.ww). # lib/ww/typ.ww is in scope deliberately — it is type.c's ww mirror, @@ -62,17 +90,21 @@ BEGIN { nviol = 0 } FNR == 1 { cur_file = FILENAME is_c = (cur_file ~ /\.(c|h)$/) - exempt_until = 0 + und_exempt_until = 0 + prim_exempt_until = 0 in_block = 0 - pending = 0 + pending_und = 0 } -# Whitelist annotation: arm the exemption window on the RAW line so an +# Whitelist annotations: arm the exemption windows on the RAW line so an # annotation inside a comment still counts. `peellint-ok` is the # already-landed sibling spelling (check.ww construction) — accepted -# as-is, history is not re-spelled. +# as-is, history is not re-spelled. The two windows are independent. tolower($0) ~ /peel(lint)?-ok([^a-z0-9_]|$)/ { - if (FNR + 9 > exempt_until) exempt_until = FNR + 9 + if (FNR + 9 > und_exempt_until) und_exempt_until = FNR + 9 +} +tolower($0) ~ /primsize-ok([^a-z0-9_]|$)/ { + if (FNR + 9 > prim_exempt_until) prim_exempt_until = FNR + 9 } # Strip comments and string/char literals by character scan: a comment @@ -110,28 +142,52 @@ tolower($0) ~ /peel(lint)?-ok([^a-z0-9_]|$)/ { } { - if (FNR <= exempt_until) { pending = 0; next } - # Both C spellings peel: p->under and (*p).under / v.under. - if (is_c) - re = "(->|\\.)[ \t]*under([^A-Za-z0-9_]|$)" - else - re = "\\.[ \t]*under([^A-Za-z0-9_]|$)" - hit = (code ~ re) - # Line-split continuation: operator at EOL, token opening the - # next code line. Comment-only lines keep the pend alive. - if (!hit && pending && code ~ /^[ \t]*under([^A-Za-z0-9_]|$)/) - hit = 1 - if (hit) { - printf("%s:%d: raw under-token read outside the chase accessor; " \ - "route via type_chase_named (C) / tichase (ww), or annotate " \ - "peel-ok: \n", cur_file, FNR) - nviol++ - } - if (code !~ /^[ \t]*$/) { + und_exempt = (FNR <= und_exempt_until) + prim_exempt = (FNR <= prim_exempt_until) + blank = (code ~ /^[ \t]*$/) + + # RULE 1 — raw under-token peel. Both C spellings peel: + # p->under and (*p).under / v.under. + if (!und_exempt) { if (is_c) - pending = (code ~ /(->|\.)[ \t]*$/) + ure = "(->|\\.)[ \t]*under([^A-Za-z0-9_]|$)" else - pending = (code ~ /\.[ \t]*$/) + ure = "\\.[ \t]*under([^A-Za-z0-9_]|$)" + uhit = (code ~ ure) + # Line-split continuation: operator at EOL, token opening the + # next code line. Comment-only lines keep the pend alive. + if (!uhit && pending_und && code ~ /^[ \t]*under([^A-Za-z0-9_]|$)/) + uhit = 1 + if (uhit) { + printf("%s:%d: raw under-token read outside the chase accessor; " \ + "route via type_chase_named (C) / tichase (ww), or annotate " \ + "peel-ok: \n", cur_file, FNR) + nviol++ + } + } + + # RULE 2 — bare primsize token (ww only). LEFT+RIGHT word bounds + # so the superstring aliasprimsize() is never a hit and a longer + # identifier with a primsize prefix is not matched. Matching the + # token (not `primsize(`) catches the paren-wrap `(primsize)(nm)`, + # the function-value bind `let p = primsize`, and every line-split — + # all compile and reintroduce the alias-blind width (review probes). + if (!is_c && !prim_exempt) { + if (code ~ /(^|[^A-Za-z0-9_])primsize([^A-Za-z0-9_]|$)/) { + printf("%s:%d: bare primsize outside the chase accessor; " \ + "route via aliasprimsize (ww), or annotate " \ + "primsize-ok: \n", cur_file, FNR) + nviol++ + } + } + + # Pending updates. An exempt line resets its pend (an annotated + # operator/name must not carry into a non-exempt next line); a blank + # or comment-only line keeps the pend alive; otherwise re-derive. + if (und_exempt) pending_und = 0 + else if (!blank) { + if (is_c) pending_und = (code ~ /(->|\.)[ \t]*$/) + else pending_und = (code ~ /\.[ \t]*$/) } }