cgen: B6-c4 cast/is/try family single peels fold into type_chase_named — 10 lines

The exact B6-c4 set (rob b6 spec §2, numbering at 4cac1cb): :9422
(N_MATCH Family-C identity-cast peel su), :9598 (match-bind base bu),
:9674 (tryprop non-call source u), :9718 (cg_ret_type peel r at the
cgexpr try region — same rt-route kinship as B5-c3's cgreturn chase:
the nullable/tagged propagate keys the chased enclosing return type
exactly as the return arms do), :9819 (tryunw twin u — LOUD-PRESERVING
ONLY, the #38b sret bound is alias-INDEPENDENT), :9986/:9991
(N_TYPEASSERT u + enum vu), :10103/:10105 (str→[]u8 cap-synth tu/fu),
:10131 (narrowing-cast tu). Raw `->under` in cgen.c 27→17 — remaining
= whitelist 5 (:731 :813/:814 :834/:835) + B7 emitters 4 + the c5
set 8, grep-verified exact.

FLIP: kb6_strcast (str→[]u8 over 2-level alias, was both-correct
divergent NO(2)) → 0/0 BYTE-ID; cs converged onto ww's UNCHANGED asm
(#263 polarity, cmp-proven) and the alias shape now equals the plain
shape (kb6_strcast_p, same hash — ken's c4 pre-test confirmed ww's
dedicated gate DOES fire on alias here, unlike the c3 sites).
kb6_try loud held both stages with the pinned #38b text (try_loud_38b
row); idcast/is/succ byte-id held; zero other movers.

TRAIN INVARIANT: cs-only; w6c_ww/ww_ww bit-identical to the 4cac1cb
baselines (b6bddc8e…/4e9ca874…). cs movers c3→c4 bounded to exactly
{kb6_strcast}; rest of corpus + five mains byte-NEUTRAL; detectors
unmoved; 989 ratchet zero flips.

944_alias_cgen_b6_run grows 14→18 rows, 38→49 checks: strcast_2lvl
flip + idcast_2lvl/is_2lvl controls + try_loud_38b both-loud pin.
All 944-family suites green; sizelint 0.
This commit is contained in:
2026-06-06 04:04:49 +09:00
parent 7a85210b13
commit c136f3c0f7
2 changed files with 87 additions and 11 deletions

View File

@@ -9399,7 +9399,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* Family C (#35): identity-cast peel — see the `is` twin. */
Node *s = cg_tagged_idcastpeel(n->lhs);
Type *st = s ? s->type : NULL;
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
Type *su = type_chase_named(st);
int is_nullable = type_isnullable(st);
int slot_size = (su && su->kind == TY_TAGGED) ? (int)su->size : 16;
int sl_off;
@@ -9574,8 +9574,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
if (cs->str && cs->str[0] && cs->type) {
Type *bt = cs->type;
Type *bu = (bt && bt->kind == TY_NAMED)
? bt->under : bt;
Type *bu = type_chase_named(bt);
if (is_nullable) {
/* Bind *T or void to a local. The
* value IS the slot's pointer word; no
@@ -9651,7 +9650,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
"return unwired (sret error-propagate is a "
"#40-family follow-up)");
Type *u = n->lhs ? n->lhs->type : NULL;
if (u && u->kind == TY_NAMED) u = u->under;
u = type_chase_named(u);
/* Family C (#35/#46): non-call sources don't fill the
* AX/DX/CX/R8 cursor the unwrap below reads — an IDENT
* loads it from its slot, a mem-based read (deref at any
@@ -9695,7 +9694,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
} else
cgexpr(c, n->lhs, locals);
Type *r = cg_ret_type;
if (r && r->kind == TY_NAMED) r = r->under;
r = type_chase_named(r);
if (u && u->kind == TY_TAGGED && u->nullable) {
char *cont = mklabel(c, "tryprop_ok");
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
@@ -9796,7 +9795,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
"unwired (mem-based unwrap is a #40-family "
"follow-up)");
Type *u = n->lhs ? n->lhs->type : NULL;
if (u && u->kind == TY_NAMED) u = u->under;
u = type_chase_named(u);
/* Family C (#35/#46): see the N_TRYPROP twin — ident loads
* the cursor from its slot, a mem-based read from the box
* address; >32B non-call stays loud (#40 family). */
@@ -9963,12 +9962,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* Family C (#35): identity-cast peel — see the `is` twin. */
Node *s = cg_tagged_idcastpeel(n->lhs);
Type *st = s ? s->type : NULL;
Type *u = (st && st->kind == TY_NAMED) ? st->under : st;
Type *u = type_chase_named(st);
Type *vt = n->type;
/* Enum ↔ integer: reinterpret-only. The value already lives
* in AX after evaluating the LHS; no tag/unwrap needed. */
{
Type *vu = (vt && vt->kind == TY_NAMED) ? vt->under : vt;
Type *vu = type_chase_named(vt);
if ((u && u->kind == TY_ENUM) ||
(vu && vu->kind == TY_ENUM)) {
cgexpr(c, s, locals);
@@ -10080,9 +10079,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
* and the receiver reads a stale value. */
{
Type *tt = n->type;
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
Type *tu = type_chase_named(tt);
Type *ft = n->lhs ? n->lhs->type : NULL;
Type *fu = (ft && ft->kind == TY_NAMED) ? ft->under : ft;
Type *fu = type_chase_named(ft);
if (tu && tu->kind == TY_SLICE
&& fu && fu->kind == TY_STR) {
ins2(c, A_MOVQ, areg(D_BX), areg(D_CX));
@@ -10108,7 +10107,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
* unsigned (Unicode scalar) and lands on the MOVL path. */
if (!from_f && !to_f && n->type) {
Type *tt = n->type;
Type *tu = (tt && tt->kind == TY_NAMED) ? tt->under : tt;
Type *tu = type_chase_named(tt);
/* Identity-width identity-sign cast is a no-op at the
* machine-int level: src and dst share both width and
* signedness, so the natural slot/load already carries