wcc_ww/cgen: c3 in-file peel sites — exprprimresolved base walk chases (B1); elemsizeofc alias-indexable leg (B3); B2 probe-cleared

F2a batch-2 c3, probe-decided per rob's spec.

B1 (exprprimresolved N_DOT, was :2073/:2075/:2078): the hand-rolled
NAMED->PTR->NAMED->STRUCT walk ran out at a 3-level alias base or a
ptr-to-2-level base — the cast-source width stayed unknowable on
wwstage only, so the #33 identity clamp was emitted where cstage
skipped it (runtime-correct both, byte-id NO, gate-blind). cs twin
castsrcprim is F1-chased at both hops (type_chase_named) -> ww-only
align-up, no fuse. Probes: b1b_3lvl + b1b_ptr2lvl graduate 0/0
byte-id; 1/2-level controls held throughout (the 2-peel covered
2 levels by accident); the FIELD-u chase is asm-neutral (cs keeps a
single peel there, sound via type_isint NAMED recursion + NAMED size
carrying the underlying's — probe b1c_fld2lvl byte-id). 944 rows
castprim_3lvl_base / castprim_ptr2lvl_base / castprim_2lvl_base_ctl.

B2 (nullableptrtag variant scan :2719): PROBE-CLEARED, NO FIX. The
2-level ptr-alias nullable variant (b|void), the order-swapped
(void|b) that defeats the return-0 fallback, AND 3-level all run 0/0
byte-id both stages — the constructible variant params never carry a
2+-level NAMED at this scan. cs twin nullable_ptr_tag (cgen.c:747)
has the identical single peel: parity, both correct on every
constructible shape. NOT nominal-sensitive (shape-keyed first-PTR
scan, no name choice exists) — the spec's #209/#211 hold condition
does not trip. The single-peel if stays, classified survivor.

B3 (elemsizeofc): an alias-NAMED INDEXABLE (`type grid = [3]cell`)
arrives as a bare N_TNAME — elemsizeof's name arm knows only
str/prims and answered the 1-sentinel through the `direct != 8`
short-circuit (#60 esz-1 family, outer-array leg). New N_TNAME leg
answers from the chased stamped tinfo via idxeffti. #83 caller
probes (rule: no unprobed caller row):
- append leg: UNREACHABLE on ww — `let xs: nums = [];` rejected by
  the wwstage CHECKER ("let: not assignable") even at 1 level; cs
  accepts+runs. check.ww is batch-4 scope -> documented on #83.
- alloc-empty leg: BOTH stages loud-reject the alias-typed binding
  hint ("cannot infer slice element type", check.ww:2547 + cs twin)
  — symmetric loud, alias-blind hint documented on #83.
- arr[i].field= float leg: plain [3]cell runs 0/0 byte-id; the
  alias-named outer array LOUDS on ww at its own site
  (cgenexpr.ww:11033 assign-resolver float-field bound), cs runs —
  fails upstream of elemsizeofc, documented on #83.
- subslice-of-alias-array call-arg probe: already 0/0 pre-change
  (the #60/#257 stamped-tinfo keying answers first) — the internal
  chase is presently LATENT defense-in-depth; ZERO callers enroll.
Remaining elemsizeofc internal loops (eti/esub) fold to tichase.

Byte-id: five-mains NEUTRAL vs the c2 build on identical inputs;
cs==ww on the regenerated five; 944 138/138; test-unit 288 green.
combined.ww regens ride along.
This commit is contained in:
2026-06-05 21:55:51 +09:00
parent f7fd3e4850
commit 018ef6680e
4 changed files with 152 additions and 27 deletions

View File

@@ -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);