diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 30ddd661..739402c2 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -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 diff --git a/test/wcc/944_alias_cgen_b6_run.c b/test/wcc/944_alias_cgen_b6_run.c index ebe4f4e7..4485e85a 100644 --- a/test/wcc/944_alias_cgen_b6_run.c +++ b/test/wcc/944_alias_cgen_b6_run.c @@ -84,6 +84,22 @@ * esub_2lvl | kb6_esub: [3]my32b narrow-elem index | * | (#61/#101-kin at the INDEX sites) — | * | latent control | 0/0 + * ---- c4 (cast/is/try: :9422 :9598 :9674 :9718 :9819 ---------- + * :9986/:9991 :10103/:10105 :10131, numbering at 4cac1cb) - + * strcast_2lvl | kb6_strcast: str→[]u8 over 2-lvl | + * | alias — was both-correct divergent; | + * | the :10103/:10105 tu/fu chase FLIPS | + * | it (cs → ww's unchanged asm, #263 | + * | polarity; alias == plain shape) | 0/0 + * idcast_2lvl | kb6_idcast: identity cast over alias | + * | union (Family-C peel) — latent | 0/0 + * is_2lvl | kb6_is: `is` over alias union — | + * | latent control | 0/0 + * try_loud_38b | kb6_try: `?` on a >48B sret-class | + * | call result — the #38b loud is | + * | ALIAS-INDEPENDENT (documented bound);| + * | :9819's chase is loud-preserving | + * | ONLY — twin texts, shared experr | err/err * * K_RUN rows build+run BOTH drivers (cs exit==cswant, ww exit==wwwant) * and assert cstage/wwstage asm byte-id. K_BUILDERR rows must FAIL @@ -338,6 +354,67 @@ static const struct row rows[] = { " if (xs[0]: i64 + xs[1]: i64 + xs[2]: i64 != 18) { return 1; };\n" " return 0;\n" "};\n", 0, 0, K_RUN, NULL, NULL }, + /* ---- c4: cast/is/try family */ + { "strcast_2lvl", + "package main;\n" + "type ms0 = str;\n" + "type ms = ms0;\n" + "export fn main() i32 = {\n" + " let a: ms = \"hey\";\n" + " let b: []u8 = a: []u8;\n" + " if (len(b) != 3) { return 1; };\n" + " if (b[0] != 'h') { return 2; };\n" + " return 0;\n" + "};\n", 0, 0, K_RUN, NULL, NULL }, + { "idcast_2lvl", + "package main;\n" + "type u0 = (void | i64);\n" + "type u = u0;\n" + "export fn main() i32 = {\n" + " let v: u = 42i64;\n" + " let w: u = (v: u);\n" + " match (w) {\n" + " case let n: i64 => { if (n != 42) { return 1; }; };\n" + " case void => { return 2; };\n" + " };\n" + " return 0;\n" + "};\n", 0, 0, K_RUN, NULL, NULL }, + { "is_2lvl", + "package main;\n" + "type u0 = (void | i64);\n" + "type u = u0;\n" + "export fn main() i32 = {\n" + " let v: u = 42i64;\n" + " if (!(v is i64)) { return 1; };\n" + " if (v is void) { return 2; };\n" + " return 0;\n" + "};\n", 0, 0, K_RUN, NULL, NULL }, + /* ALIAS-INDEPENDENT #38b bound (ken's row is >48B sret-class): + * the :9819 chase must stay loud-preserving — this pins the + * fatal so a future chase can't silently green it. */ + { "try_loud_38b", + "package main;\n" + "type e0 = !i64;\n" + "type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64 };\n" + "type r0 = (big | e0);\n" + "type r = r0;\n" + "fn w() r = {\n" + " let s: big;\n" + " s.a = 1; s.b = 2; s.c = 3; s.d = 4; s.e = 5; s.f = 6; s.g = 77;\n" + " return s;\n" + "};\n" + "fn outer() (i64 | e0) = {\n" + " let v = w()?;\n" + " return v.g;\n" + "};\n" + "export fn main() i32 = {\n" + " match (outer()) {\n" + " case let n: i64 => { if (n != 77) { return 1; }; };\n" + " case e0 => { return 2; };\n" + " };\n" + " return 0;\n" + "};\n", 0, 0, K_BUILDERR, + "#38b: `?`/`!` on an sret-class call result unwired", NULL }, }; static int