diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 4eef3353..72a15669 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -17600,9 +17600,28 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { if (nk == nkind.N_TARRAY) { nest = t.lhs; }; if (nest != nil && nest.kind == nkind.N_TARRAY) { let eti: *tinfo = nest.type_: *tinfo; - for (eti != nil && eti.kind == tykind.TY_NAMED) { eti = eti.under; }; + eti = tichase(eti); if (eti != nil) { return eti.size: i32; }; }; + // #83/B3: an alias-NAMED INDEXABLE (`type grid = [3]cell`) arrives + // as a bare N_TNAME — elemsizeof's name arm knows only str + prims + // and answers the 1-sentinel, so the `direct != 8` short-circuit + // below returned 1 and every caller strode by one byte (the #60 + // esz-1 family, outer-array leg). Answer from the chased stamped + // tinfo via idxeffti (which also drills an alias-of-`*[N]T`), + // element chased like the #8 leg below. Non-indexable names + // (struct/prim/str aliases) fall through to the old paths — + // byte-id preserved there. + if (nk == nkind.N_TNAME) { + let eff: *tinfo = idxeffti(t.type_: *tinfo); + if (eff != nil) { + if (eff.kind == tykind.TY_ARRAY + || eff.kind == tykind.TY_SLICE) { + let aes: *tinfo = tichase(eff.sub); + if (aes != nil) { return aes.size: i32; }; + }; + }; + }; let direct: i32 = elemsizeof(t); if (direct != 8) { return direct; }; // #8: direct==8 is elemsizeof's "unresolved alias/aggregate" sentinel. @@ -17622,7 +17641,7 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { let ti: *tinfo = idxeffti(t.type_: *tinfo); if (ti != nil) { let esub: *tinfo = ti.sub; - for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; }; + esub = tichase(esub); if (esub != nil) { return esub.size: i32; }; }; let elem: *node = idxelemtn(t); @@ -18137,17 +18156,25 @@ export fn exprprimresolved(c: *cgen, n: *node, // 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. + // N_DOT's stamped type_ (check.ww:2012). typeisint ? size : 0; + // bool falls out because typeisint(bool) is false — the same + // exclusion the old streq("bool") arm encoded. + // B2-c3/B1: the base walk CHASES each hop (was a hand-rolled + // 2-peel that ran out at a 3-level alias base or a ptr-to- + // 2-level base — clamp kept on a knowable identity cast, + // runtime-correct but cs!=ww asm). Aligns up to cstage + // castsrcprim post-F1 (type_chase_named at both hops). The + // field-u chase is asm-neutral: cs keeps its single peel + // there, sound through type_isint's NAMED recursion + the + // NAMED tinfo carrying its underlying's size (probe + // b1c_fld2lvl byte-id). 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; }; + bu = tichase(bu); + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = tichase(bu.sub); }; 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; }; + u = tichase(u); if (typeisint(u)) { *sz_out = u.size: i32; *unsigned_out = typeisunsigned(u); diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 71eea187..eb45286b 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1528,9 +1528,28 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { if (nk == nkind.N_TARRAY) { nest = t.lhs; }; if (nest != nil && nest.kind == nkind.N_TARRAY) { let eti: *tinfo = nest.type_: *tinfo; - for (eti != nil && eti.kind == tykind.TY_NAMED) { eti = eti.under; }; + eti = tichase(eti); if (eti != nil) { return eti.size: i32; }; }; + // #83/B3: an alias-NAMED INDEXABLE (`type grid = [3]cell`) arrives + // as a bare N_TNAME — elemsizeof's name arm knows only str + prims + // and answers the 1-sentinel, so the `direct != 8` short-circuit + // below returned 1 and every caller strode by one byte (the #60 + // esz-1 family, outer-array leg). Answer from the chased stamped + // tinfo via idxeffti (which also drills an alias-of-`*[N]T`), + // element chased like the #8 leg below. Non-indexable names + // (struct/prim/str aliases) fall through to the old paths — + // byte-id preserved there. + if (nk == nkind.N_TNAME) { + let eff: *tinfo = idxeffti(t.type_: *tinfo); + if (eff != nil) { + if (eff.kind == tykind.TY_ARRAY + || eff.kind == tykind.TY_SLICE) { + let aes: *tinfo = tichase(eff.sub); + if (aes != nil) { return aes.size: i32; }; + }; + }; + }; let direct: i32 = elemsizeof(t); if (direct != 8) { return direct; }; // #8: direct==8 is elemsizeof's "unresolved alias/aggregate" sentinel. @@ -1550,7 +1569,7 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { let ti: *tinfo = idxeffti(t.type_: *tinfo); if (ti != nil) { let esub: *tinfo = ti.sub; - for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; }; + esub = tichase(esub); if (esub != nil) { return esub.size: i32; }; }; let elem: *node = idxelemtn(t); @@ -2065,17 +2084,25 @@ export fn exprprimresolved(c: *cgen, n: *node, // 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. + // N_DOT's stamped type_ (check.ww:2012). typeisint ? size : 0; + // bool falls out because typeisint(bool) is false — the same + // exclusion the old streq("bool") arm encoded. + // B2-c3/B1: the base walk CHASES each hop (was a hand-rolled + // 2-peel that ran out at a 3-level alias base or a ptr-to- + // 2-level base — clamp kept on a knowable identity cast, + // runtime-correct but cs!=ww asm). Aligns up to cstage + // castsrcprim post-F1 (type_chase_named at both hops). The + // field-u chase is asm-neutral: cs keeps its single peel + // there, sound through type_isint's NAMED recursion + the + // NAMED tinfo carrying its underlying's size (probe + // b1c_fld2lvl byte-id). 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; }; + bu = tichase(bu); + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = tichase(bu.sub); }; 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; }; + u = tichase(u); if (typeisint(u)) { *sz_out = u.size: i32; *unsigned_out = typeisunsigned(u); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 18e9897d..0ea6fce9 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -17600,9 +17600,28 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { if (nk == nkind.N_TARRAY) { nest = t.lhs; }; if (nest != nil && nest.kind == nkind.N_TARRAY) { let eti: *tinfo = nest.type_: *tinfo; - for (eti != nil && eti.kind == tykind.TY_NAMED) { eti = eti.under; }; + eti = tichase(eti); if (eti != nil) { return eti.size: i32; }; }; + // #83/B3: an alias-NAMED INDEXABLE (`type grid = [3]cell`) arrives + // as a bare N_TNAME — elemsizeof's name arm knows only str + prims + // and answers the 1-sentinel, so the `direct != 8` short-circuit + // below returned 1 and every caller strode by one byte (the #60 + // esz-1 family, outer-array leg). Answer from the chased stamped + // tinfo via idxeffti (which also drills an alias-of-`*[N]T`), + // element chased like the #8 leg below. Non-indexable names + // (struct/prim/str aliases) fall through to the old paths — + // byte-id preserved there. + if (nk == nkind.N_TNAME) { + let eff: *tinfo = idxeffti(t.type_: *tinfo); + if (eff != nil) { + if (eff.kind == tykind.TY_ARRAY + || eff.kind == tykind.TY_SLICE) { + let aes: *tinfo = tichase(eff.sub); + if (aes != nil) { return aes.size: i32; }; + }; + }; + }; let direct: i32 = elemsizeof(t); if (direct != 8) { return direct; }; // #8: direct==8 is elemsizeof's "unresolved alias/aggregate" sentinel. @@ -17622,7 +17641,7 @@ fn elemsizeofc(c: *cgen, t: *node) i32 = { let ti: *tinfo = idxeffti(t.type_: *tinfo); if (ti != nil) { let esub: *tinfo = ti.sub; - for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; }; + esub = tichase(esub); if (esub != nil) { return esub.size: i32; }; }; let elem: *node = idxelemtn(t); @@ -18137,17 +18156,25 @@ export fn exprprimresolved(c: *cgen, n: *node, // 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. + // N_DOT's stamped type_ (check.ww:2012). typeisint ? size : 0; + // bool falls out because typeisint(bool) is false — the same + // exclusion the old streq("bool") arm encoded. + // B2-c3/B1: the base walk CHASES each hop (was a hand-rolled + // 2-peel that ran out at a 3-level alias base or a ptr-to- + // 2-level base — clamp kept on a knowable identity cast, + // runtime-correct but cs!=ww asm). Aligns up to cstage + // castsrcprim post-F1 (type_chase_named at both hops). The + // field-u chase is asm-neutral: cs keeps its single peel + // there, sound through type_isint's NAMED recursion + the + // NAMED tinfo carrying its underlying's size (probe + // b1c_fld2lvl byte-id). 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; }; + bu = tichase(bu); + if (bu != nil && bu.kind == tykind.TY_PTR) { bu = tichase(bu.sub); }; 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; }; + u = tichase(u); if (typeisint(u)) { *sz_out = u.size: i32; *unsigned_out = typeisunsigned(u); diff --git a/test/wcc/944_alias_accept_run.c b/test/wcc/944_alias_accept_run.c index 182dfb88..f4347076 100644 --- a/test/wcc/944_alias_accept_run.c +++ b/test/wcc/944_alias_accept_run.c @@ -753,6 +753,50 @@ static const struct row rows[] = { " };\n" " return 0;\n" "};\n", 0, K_RUN, NULL }, + /* ---- F2a batch-2 c3/B1: exprprimresolved's N_DOT base walk was + * a hand-rolled 2-peel — a 3-level alias base (or ptr-to-2-level) + * left the cast-source width unknowable on wwstage only, so the + * #33 identity clamp stayed emitted where cstage (castsrcprim, + * F1-chased) skipped it: runtime-correct both, byte-id NO. + * Graduated by the tichase walk; 1/2-level controls held all + * along (the 2-peel covered them by accident). */ + { "castprim_3lvl_base", + "package main;\n" + "type s1 = struct { f: u32 };\n" + "type s2 = s1;\n" + "type s3 = s2;\n" + "export fn main() i32 = {\n" + " let x: s3;\n" + " x.f = 70000;\n" + " let y: u64 = (x.f: u32): u64;\n" + " if (y != 70000) { return 1; };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, + { "castprim_ptr2lvl_base", + "package main;\n" + "type s1 = struct { f: u32 };\n" + "type s2 = s1;\n" + "export fn main() i32 = {\n" + " let x: s2;\n" + " x.f = 70000;\n" + " let p: *s2 = &x;\n" + " let y: u64 = (p.f: u32): u64;\n" + " if (y != 70000) { return 1; };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, + /* 2-level control (was already byte-id pre-c3; pins the accident + * out of existence). */ + { "castprim_2lvl_base_ctl", + "package main;\n" + "type s1 = struct { f: u32 };\n" + "type s2 = s1;\n" + "export fn main() i32 = {\n" + " let x: s2;\n" + " x.f = 70000;\n" + " let y: u64 = (x.f: u32): u64;\n" + " if (y != 70000) { return 1; };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, /* ---- task #71 (rob probe-ruled F1 enrollment): nested fields * whose TYPES are aliases. The chained-dot store/read/addr-of * walks single-peeled each hop, aborted on the alias, and fell