diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index c3de178e..42d79f33 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -4053,8 +4053,9 @@ cgexpr(Cg *c, Node *n, Local *locals) int abort = 0; while (cur && cur->kind == N_DOT && cur->lhs) { Type *pt = cur->lhs->type; - Type *pu = (pt && pt->kind == TY_NAMED) - ? pt->under : pt; + /* Transitive chase (#71) — addr-of sibling of the + * chained-dot walks. */ + Type *pu = type_chase_named(pt); if (!pu) { abort = 1; break; } if (cur == opnd && (pu->kind == TY_SLICE || pu->kind == TY_STR)) { @@ -5517,8 +5518,12 @@ cgexpr(Cg *c, Node *n, Local *locals) int abort = 0; while (cur && cur->kind == N_DOT && cur->lhs) { Type *pt = cur->lhs->type; - Type *pu = (pt && pt->kind == TY_NAMED) - ? pt->under : pt; + /* Transitive chase (#71): an alias-typed hop left pu + * TY_NAMED -> abort -> the generic cgplaceaddr spine + * (PUSHQ/LEAQ/ADDQ/POPQ) while wwstage emits the direct + * offset store. Runtime-correct both; chasing converges + * cs onto the fast direct arm = wwstage's asm. */ + Type *pu = type_chase_named(pt); if (!pu) { abort = 1; break; } if (cur == n->lhs && (pu->kind == TY_SLICE || pu->kind == TY_STR)) { @@ -5530,8 +5535,7 @@ cgexpr(Cg *c, Node *n, Local *locals) /* `*T` root: dereference at emit time; * walk through pointee struct fields. * Last-hop only (root is a bare ident). */ - Type *sub = (pu->sub->kind == TY_NAMED) - ? pu->sub->under : pu->sub; + Type *sub = type_chase_named(pu->sub); if (sub && sub->kind == TY_STRUCT) { pu = sub; ptr_root = 1; @@ -10298,7 +10302,10 @@ cgexpr(Cg *c, Node *n, Local *locals) * stale. Sibling of the pointer-chain branch further down. */ if (n->lhs && n->lhs->kind == N_DOT) { Type *lt0 = n->lhs->type; - Type *lu0 = (lt0 && lt0->kind == TY_NAMED) ? lt0->under : lt0; + /* Transitive chase (#71) — the READ twin of the chained-dot + * store walk; an alias-typed hop skipped this arm onto the + * ADDQ-per-hop address spine while wwstage folds the offsets. */ + Type *lu0 = type_chase_named(lt0); int leaf_is_pseudo = lu0 && n->str && (lu0->kind == TY_SLICE || lu0->kind == TY_STR) && (strcmp(n->str, "ptr") == 0 @@ -10313,8 +10320,7 @@ cgexpr(Cg *c, Node *n, Local *locals) int abort = 0; while (cur && cur->kind == N_DOT && cur->lhs) { Type *pt = cur->lhs->type; - Type *pu = (pt && pt->kind == TY_NAMED) - ? pt->under : pt; + Type *pu = type_chase_named(pt); if (!pu) { abort = 1; break; } if (cur == n && (pu->kind == TY_SLICE || pu->kind == TY_STR)) { @@ -10329,8 +10335,7 @@ cgexpr(Cg *c, Node *n, Local *locals) * is a bare ident) — `*T`-field mid- * chain keeps its cgexpr-based pointer- * field branch further down. */ - Type *sub = (pu->sub->kind == TY_NAMED) - ? pu->sub->under : pu->sub; + Type *sub = type_chase_named(pu->sub); if (sub && sub->kind == TY_STRUCT) { pu = sub; ptr_root = 1; diff --git a/test/wcc/944_alias_accept_run.c b/test/wcc/944_alias_accept_run.c index 37d23157..1de3d89c 100644 --- a/test/wcc/944_alias_accept_run.c +++ b/test/wcc/944_alias_accept_run.c @@ -50,6 +50,10 @@ * | store/push word0-only; CS-ONLY until | * | F2 lands the wwstage half; base ctl | * | + the #54-bound `as` row pinned loud | 0(cs) + * nested_alias_field_* | task #71: alias-typed nested-field | + * | store/read/addr-of walks fold to | + * | direct offsets (byte-id graduation, | + * | both decl orders) | 0 * * K_RUN rows also assert cstage/wwstage asm byte-id (acceptance * graduations land byte-identical — F0's accept side was wwstage). @@ -275,10 +279,9 @@ static const struct row rows[] = { * noise() call clobbers BX/CX so stale-register luck can't pass a * word0-only header. Unprobed sibling gates are LOUD-tripwired, * pinned by the #73 row at the end of this block. Rows whose - * `.len` readback rides the chained-dot READ walk are K_RUN_CS - * here (runtime-correct both stages; cs takes the generic spine - * pre-#71) and flip to K_RUN at the #71 walk-chase commit, which - * graduates their byte-id. */ + * `.len` readback rides the chained-dot READ walk entered as + * K_RUN_CS (runtime-correct both stages; cs took the generic + * spine) and graduated to K_RUN with the #71 walk chase. */ { "slicefield_store_2lvl", "package main;\n" "type sl = []int;\n" @@ -294,7 +297,7 @@ static const struct row rows[] = { " if (b.s[2] != 900) { return 2; };\n" " if (b.s.len != 3) { return 3; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_range_2lvl", "package main;\n" "type sl = []int;\n" @@ -311,7 +314,7 @@ static const struct row rows[] = { " if (x < 700) { return 3; };\n" " };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "strfield_store_2lvl", "package main;\n" "type s1t = str;\n" @@ -323,7 +326,7 @@ static const struct row rows[] = { " b.n = 5;\n" " if (b.s.len != 5) { return 1; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_structlit_2lvl", "package main;\n" "type sl = []int;\n" @@ -336,7 +339,7 @@ static const struct row rows[] = { " if (b.s.len != 3) { return 2; };\n" " if (b.s[2] != 900) { return 3; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_viaptr_2lvl", "package main;\n" "type sl = []int;\n" @@ -350,7 +353,7 @@ static const struct row rows[] = { " if (p.s.len != 3) { return 1; };\n" " if (p.s[2] != 900) { return 2; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_chainstore_2lvl", "package main;\n" "type sl = []int;\n" @@ -364,7 +367,7 @@ static const struct row rows[] = { " if (v.i.s.len != 3) { return 1; };\n" " if (v.i.s[2] != 900) { return 2; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_ptrchain_2lvl", "package main;\n" "type sl = []int;\n" @@ -381,7 +384,7 @@ static const struct row rows[] = { " if (i.s.len != 3) { return 1; };\n" " if (i.s[2] != 900) { return 2; };\n" " return 0;\n" - "};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */ + "};\n", 0, K_RUN, NULL }, { "slicefield_chainread_2lvl", "package main;\n" "type sl = []int;\n" @@ -749,6 +752,42 @@ static const struct row rows[] = { " };\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 + * to the generic PUSHQ/LEAQ/ADDQ/POPQ address spine — runtime- + * correct but byte-id NO vs wwstage's folded direct offsets. + * Chasing the walks converges cs onto the direct arm = ww's asm. + * Distinct values; the LAST field of the SECOND member checked. */ + { "nested_alias_field_norm", + "package main;\n" + "type fa = inner;\n" + "type inner = struct { a: size, b: size };\n" + "type outer = struct { x: fa, y: fa };\n" + "export fn main() i32 = {\n" + " let v: outer;\n" + " v.x.a = 4; v.x.b = 9;\n" + " v.y.a = 7; v.y.b = 3;\n" + " if (v.x.b != 9) { return 1; };\n" + " if (v.y.b != 3) { return 2; };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, + { "nested_alias_field_fwd", + "package main;\n" + "type outer = struct { x: fa, y: fa };\n" + "type fa = inner;\n" + "type inner = struct { a: size, b: size };\n" + "export fn main() i32 = {\n" + " let v: outer;\n" + " v.x.a = 4; v.x.b = 9;\n" + " v.y.a = 7; v.y.b = 3;\n" + " if (v.x.b != 9) { return 1; };\n" + " if (v.y.b != 3) { return 2; };\n" + " let pa = &v.y.b;\n" + " *pa = 11;\n" + " if (v.y.b != 11) { return 3; };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, /* banked row L2-3 (`v as ali` readback) trips task #54's as-binding * aggregate-init bound — LOUD both stages today; fold the row into * #54's pin set on its fold (do not drop). Pinned here as the