diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 7281fecc..53f0a8f7 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -5217,6 +5217,26 @@ cgexpr(Cg *c, Node *n, Local *locals) int store_op = fldstoreop(ft, fsz); int foff = (int)f->offset; if (n->op == TK_ASSIGN) { + /* tagged leaf (#38a): eval the *struct + * base into BX, then the shared widener + * (it spills BX across its internal + * cgexpr) — same base-then-widen order + * as the single-dot via_ptr arm. The + * scalar tail below stored ONE sized + * word at the field offset: the rhs + * landed in the TAG slot (ken b8: + * `o.p.min = 8: size` left `is size` + * false). */ + if (fu && fu->kind == TY_TAGGED) { + cgexpr(c, n->lhs->lhs, locals); + ins2(c, A_MOVQ, areg(D_AX), + areg(D_BX)); + cg_widen_tagged_store(c, + &locals, fu, n->rhs, + D_BX, foff, + (int)fu->size); + break; + } int c_isf32 = 0; if (fld_isfloat(ft, &c_isf32)) { /* f64/f32 chained-store: cgexpr rhs @@ -5501,6 +5521,39 @@ cgexpr(Cg *c, Node *n, Local *locals) int fsz = (int)(leaf_type ? leaf_type->size : 8); int store_op = fldstoreop(leaf_type, fsz); + /* tagged leaf (#38a): full slot rewrite via + * the shared widener — the single-dot + * tagged-field arm (after_dot_assign) + * verbatim. The scalar tail below stored ONE + * sized word at the field offset: the rhs + * landed in the TAG slot and the payload + * kept its old bytes (ken x5d: + * `o.r.min = 8: size` left `is size` + * false). Only plain `=` reaches this + * walker (TK_ASSIGN gate above). */ + if (fu && fu->kind == TY_TAGGED) { + int tsz = (int)fu->size; + if (via_cx) { + if (ptr_root) + ins2(c, A_MOVQ, + amem(D_BP, base_disp), + areg(D_BX)); + else + ins2(c, A_LEAQ, + masym(c, cur->str), + areg(D_BX)); + cg_widen_tagged_store(c, + &locals, fu, n->rhs, + D_BX, total_off, tsz); + } else { + cg_widen_tagged_store(c, + &locals, fu, n->rhs, + D_BP, + base_disp + total_off, + tsz); + } + break; + } if (fu && (fu->kind == TY_STR || fu->kind == TY_SLICE)) { /* str/slice: store ptr/len/cap. cgexpr @@ -10225,6 +10278,42 @@ cgexpr(Cg *c, Node *n, Local *locals) Type *fu = (leaf_type && leaf_type->kind == TY_NAMED) ? leaf_type->under : leaf_type; + /* tagged leaf (#38a): load the box into + * the tagged cursor (AX=tag, DX=val0, + * R8=val2 before CX=val1 — base_reg may + * be CX), the single-dot tagged-field arm + * verbatim. Pre-#38a the fldloadop tail + * pulled ONE word (the tag): is-tests + * passed by tag-luck while as/match/let + * consumers read stale payload registers + * (ken x5c: o.r.min as size added DX). + * >32B box: ADDRESS in AX (the #37 + * cg_tagged_memread convention). */ + if (fu && fu->kind == TY_TAGGED) { + int fo = base_disp + total_off; + if ((int)fu->size + > TUPLE_GPCAP * 8) { + ins2(c, A_LEAQ, + amem(base_reg, fo), + areg(D_AX)); + goto dot_done; + } + ins2(c, A_MOVQ, + amem(base_reg, fo + 0), + areg(D_AX)); + ins2(c, A_MOVQ, + amem(base_reg, fo + 8), + areg(D_DX)); + if (fu->size > 24) + ins2(c, A_MOVQ, + amem(base_reg, fo + 24), + areg(D_R8)); + if (fu->size > 16) + ins2(c, A_MOVQ, + amem(base_reg, fo + 16), + areg(D_CX)); + goto dot_done; + } if (fu && fu->kind == TY_STR) { ins2(c, A_MOVQ, amem(base_reg, @@ -10729,6 +10818,40 @@ cgexpr(Cg *c, Node *n, Local *locals) Type *ft = f->type; Type *fu = (ft && ft->kind == TY_NAMED) ? ft->under : ft; + /* tagged leaf (#38a): AX holds the *struct + * base and the tagged cursor targets AX + * (tag) — stage the base in BX, then the + * cursor load (AX=tag, DX=val0, R8=val2, + * CX=val1; >32B → ADDRESS in AX, #37). The + * fldloadop tail pulled ONE word (ken b8: + * o.p.min read stale DX as payload). */ + if (fu && fu->kind == TY_TAGGED) { + int fo = (int)f->offset; + ins2(c, A_MOVQ, areg(D_AX), + areg(D_BX)); + if ((int)fu->size + > TUPLE_GPCAP * 8) { + ins2(c, A_LEAQ, + amem(D_BX, fo), + areg(D_AX)); + goto dot_done; + } + ins2(c, A_MOVQ, + amem(D_BX, fo + 0), + areg(D_AX)); + ins2(c, A_MOVQ, + amem(D_BX, fo + 8), + areg(D_DX)); + if (fu->size > 24) + ins2(c, A_MOVQ, + amem(D_BX, fo + 24), + areg(D_R8)); + if (fu->size > 16) + ins2(c, A_MOVQ, + amem(D_BX, fo + 16), + areg(D_CX)); + goto dot_done; + } /* str IS []u8 — same 3-word {ptr,len,cap} as a * slice field: load (ptr, len, cap) into * (AX, BX, CX). AX is the *struct base, so diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index fedb9892..1bb85c5a 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): load the box into the tagged + // cursor via cgloadtaggedfield (AX=tag, DX=word0, + // R8=word2 before CX=word1 — CX may be the base; + // >32B box leaves its ADDRESS in AX, the #37 + // convention) — the single-dot tagged-field arm + // verbatim. Pre-#38a the scalar loadopsz tail pulled + // ONE word (the tag): is-tests passed by tag-luck + // while as/match/let consumers read stale payload + // registers (ken x5c: o.r.min as size added DX). + if (typeistagged(leaftype)) { + let tlu: *tinfo = leaftype; + for (tlu != nil && tlu.kind == tykind.TY_NAMED) { + tlu = tlu.under; + }; + let ttsz: i32 = tlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), CX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), CX\n"); + }; + cgloadtaggedfield(c, "CX", totaloff, ttsz); + } else { + cgloadtaggedfield(c, "BP", + rootoff + totaloff, ttsz); + }; + return; + }; if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { @@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; cgexpr(c, lhs); // AX = ptr to inner struct + // tagged leaf (#38a): AX holds the + // *struct base and the tagged cursor + // targets AX (tag) — stage the base in + // BX, then cgloadtaggedfield (cstage + // chained-*struct twin; ken b8: the + // scalar tail read stale DX as payload). + if (typeistagged(ft)) { + let plu: *tinfo = ft; + for (plu != nil && plu.kind == tykind.TY_NAMED) { + plu = plu.under; + }; + emitline("\tMOVQ\tAX, BX\n"); + cgloadtaggedfield(c, "BX", + tf.offset: i32, + plu.size: i32); + return; + }; // str IS []u8 — same 3-word {ptr,len,cap} // as a slice field: load (ptr, len, cap) // into (AX, BX, CX). AX is the *struct @@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; if (n.op == tkind.TK_ASSIGN) { + // tagged leaf (#38a): eval the *struct + // base into BX, then the shared widener + // (it spills BX across its internal + // cgexpr) — same base-then-widen order + // as the single-dot via-ptr arm. The + // scalar tail below stored ONE sized + // word at the field offset: the rhs + // landed in the TAG slot (ken b8). + if (typeistagged(ft)) { + let flu: *tinfo = ft; + for (flu != nil && flu.kind == tykind.TY_NAMED) { + flu = flu.under; + }; + cgexpr(c, base); + emitline("\tMOVQ\tAX, BX\n"); + cgwidentaggedstore(c, flu, n.rhs, + "BX", tf.offset: i32, + flu.size: i32); + return; + }; if (typeisstr(ft) || typeisslice(ft)) { // str/slice: rhs leaves AX=ptr, // BX=len, CX=cap (#1/Phase 3). Spill @@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): full slot rewrite via the shared + // widener — the single-dot tagged-field arm verbatim + // (cgwidentaggedstore spills the BX base itself). The + // scalar tail below stored ONE sized word at the field + // offset: the rhs landed in the TAG slot and the payload + // kept its old bytes (ken x5d: `o.r.min = 8: size` left + // `is size` false). Only plain `=` reaches this walker + // (TK_ASSIGN gate above). + if (typeistagged(leaftype)) { + let wlu: *tinfo = leaftype; + for (wlu != nil && wlu.kind == tykind.TY_NAMED) { + wlu = wlu.under; + }; + let wtsz: i32 = wlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), BX\n"); + }; + cgwidentaggedstore(c, wlu, n.rhs, + "BX", totaloff, wtsz); + } else { + cgwidentaggedstore(c, wlu, n.rhs, + "BP", rootoff + totaloff, wtsz); + }; + return; + }; if (typeisstr(leaftype) || typeisslice(leaftype)) { // str/slice: store ptr/len/cap. cgexpr leaves // CX=cap, so the viacx base goes in DX (not CX) to diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index e2a42eb2..838341c5 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -3594,6 +3594,38 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): load the box into the tagged + // cursor via cgloadtaggedfield (AX=tag, DX=word0, + // R8=word2 before CX=word1 — CX may be the base; + // >32B box leaves its ADDRESS in AX, the #37 + // convention) — the single-dot tagged-field arm + // verbatim. Pre-#38a the scalar loadopsz tail pulled + // ONE word (the tag): is-tests passed by tag-luck + // while as/match/let consumers read stale payload + // registers (ken x5c: o.r.min as size added DX). + if (typeistagged(leaftype)) { + let tlu: *tinfo = leaftype; + for (tlu != nil && tlu.kind == tykind.TY_NAMED) { + tlu = tlu.under; + }; + let ttsz: i32 = tlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), CX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), CX\n"); + }; + cgloadtaggedfield(c, "CX", totaloff, ttsz); + } else { + cgloadtaggedfield(c, "BP", + rootoff + totaloff, ttsz); + }; + return; + }; if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { @@ -3823,6 +3855,23 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; cgexpr(c, lhs); // AX = ptr to inner struct + // tagged leaf (#38a): AX holds the + // *struct base and the tagged cursor + // targets AX (tag) — stage the base in + // BX, then cgloadtaggedfield (cstage + // chained-*struct twin; ken b8: the + // scalar tail read stale DX as payload). + if (typeistagged(ft)) { + let plu: *tinfo = ft; + for (plu != nil && plu.kind == tykind.TY_NAMED) { + plu = plu.under; + }; + emitline("\tMOVQ\tAX, BX\n"); + cgloadtaggedfield(c, "BX", + tf.offset: i32, + plu.size: i32); + return; + }; // str IS []u8 — same 3-word {ptr,len,cap} // as a slice field: load (ptr, len, cap) // into (AX, BX, CX). AX is the *struct @@ -9266,6 +9315,26 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; if (n.op == tkind.TK_ASSIGN) { + // tagged leaf (#38a): eval the *struct + // base into BX, then the shared widener + // (it spills BX across its internal + // cgexpr) — same base-then-widen order + // as the single-dot via-ptr arm. The + // scalar tail below stored ONE sized + // word at the field offset: the rhs + // landed in the TAG slot (ken b8). + if (typeistagged(ft)) { + let flu: *tinfo = ft; + for (flu != nil && flu.kind == tykind.TY_NAMED) { + flu = flu.under; + }; + cgexpr(c, base); + emitline("\tMOVQ\tAX, BX\n"); + cgwidentaggedstore(c, flu, n.rhs, + "BX", tf.offset: i32, + flu.size: i32); + return; + }; if (typeisstr(ft) || typeisslice(ft)) { // str/slice: rhs leaves AX=ptr, // BX=len, CX=cap (#1/Phase 3). Spill @@ -9473,6 +9542,38 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): full slot rewrite via the shared + // widener — the single-dot tagged-field arm verbatim + // (cgwidentaggedstore spills the BX base itself). The + // scalar tail below stored ONE sized word at the field + // offset: the rhs landed in the TAG slot and the payload + // kept its old bytes (ken x5d: `o.r.min = 8: size` left + // `is size` false). Only plain `=` reaches this walker + // (TK_ASSIGN gate above). + if (typeistagged(leaftype)) { + let wlu: *tinfo = leaftype; + for (wlu != nil && wlu.kind == tykind.TY_NAMED) { + wlu = wlu.under; + }; + let wtsz: i32 = wlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), BX\n"); + }; + cgwidentaggedstore(c, wlu, n.rhs, + "BX", totaloff, wtsz); + } else { + cgwidentaggedstore(c, wlu, n.rhs, + "BP", rootoff + totaloff, wtsz); + }; + return; + }; if (typeisstr(leaftype) || typeisslice(leaftype)) { // str/slice: store ptr/len/cap. cgexpr leaves // CX=cap, so the viacx base goes in DX (not CX) to diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 41d71a11..00271e58 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -24438,6 +24438,38 @@ fn cgdot(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): load the box into the tagged + // cursor via cgloadtaggedfield (AX=tag, DX=word0, + // R8=word2 before CX=word1 — CX may be the base; + // >32B box leaves its ADDRESS in AX, the #37 + // convention) — the single-dot tagged-field arm + // verbatim. Pre-#38a the scalar loadopsz tail pulled + // ONE word (the tag): is-tests passed by tag-luck + // while as/match/let consumers read stale payload + // registers (ken x5c: o.r.min as size added DX). + if (typeistagged(leaftype)) { + let tlu: *tinfo = leaftype; + for (tlu != nil && tlu.kind == tykind.TY_NAMED) { + tlu = tlu.under; + }; + let ttsz: i32 = tlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), CX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), CX\n"); + }; + cgloadtaggedfield(c, "CX", totaloff, ttsz); + } else { + cgloadtaggedfield(c, "BP", + rootoff + totaloff, ttsz); + }; + return; + }; if (typeisstr(leaftype)) { if (viacx) { if (ptrroot) { @@ -24667,6 +24699,23 @@ fn cgdot(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; cgexpr(c, lhs); // AX = ptr to inner struct + // tagged leaf (#38a): AX holds the + // *struct base and the tagged cursor + // targets AX (tag) — stage the base in + // BX, then cgloadtaggedfield (cstage + // chained-*struct twin; ken b8: the + // scalar tail read stale DX as payload). + if (typeistagged(ft)) { + let plu: *tinfo = ft; + for (plu != nil && plu.kind == tykind.TY_NAMED) { + plu = plu.under; + }; + emitline("\tMOVQ\tAX, BX\n"); + cgloadtaggedfield(c, "BX", + tf.offset: i32, + plu.size: i32); + return; + }; // str IS []u8 — same 3-word {ptr,len,cap} // as a slice field: load (ptr, len, cap) // into (AX, BX, CX). AX is the *struct @@ -30110,6 +30159,26 @@ fn cgassign(c: *cgen, n: *node) void = { if (streq(tf.name, fld)) { let ft: *tinfo = tf.type_; if (n.op == tkind.TK_ASSIGN) { + // tagged leaf (#38a): eval the *struct + // base into BX, then the shared widener + // (it spills BX across its internal + // cgexpr) — same base-then-widen order + // as the single-dot via-ptr arm. The + // scalar tail below stored ONE sized + // word at the field offset: the rhs + // landed in the TAG slot (ken b8). + if (typeistagged(ft)) { + let flu: *tinfo = ft; + for (flu != nil && flu.kind == tykind.TY_NAMED) { + flu = flu.under; + }; + cgexpr(c, base); + emitline("\tMOVQ\tAX, BX\n"); + cgwidentaggedstore(c, flu, n.rhs, + "BX", tf.offset: i32, + flu.size: i32); + return; + }; if (typeisstr(ft) || typeisslice(ft)) { // str/slice: rhs leaves AX=ptr, // BX=len, CX=cap (#1/Phase 3). Spill @@ -30317,6 +30386,38 @@ fn cgassign(c: *cgen, n: *node) void = { }; return; }; + // tagged leaf (#38a): full slot rewrite via the shared + // widener — the single-dot tagged-field arm verbatim + // (cgwidentaggedstore spills the BX base itself). The + // scalar tail below stored ONE sized word at the field + // offset: the rhs landed in the TAG slot and the payload + // kept its old bytes (ken x5d: `o.r.min = 8: size` left + // `is size` false). Only plain `=` reaches this walker + // (TK_ASSIGN gate above). + if (typeistagged(leaftype)) { + let wlu: *tinfo = leaftype; + for (wlu != nil && wlu.kind == tykind.TY_NAMED) { + wlu = wlu.under; + }; + let wtsz: i32 = wlu.size: i32; + if (viacx) { + if (ptrroot) { + emitline("\tMOVQ\t"); + emitoff(rootoff: i64); + emitline("(BP), BX\n"); + } else { + emitline("\tLEAQ\t"); + emitsymname(c, rootname); + emitline("(SB), BX\n"); + }; + cgwidentaggedstore(c, wlu, n.rhs, + "BX", totaloff, wtsz); + } else { + cgwidentaggedstore(c, wlu, n.rhs, + "BP", rootoff + totaloff, wtsz); + }; + return; + }; if (typeisstr(leaftype) || typeisslice(leaftype)) { // str/slice: store ptr/len/cap. cgexpr leaves // CX=cap, so the viacx base goes in DX (not CX) to diff --git a/test/wcc/938_tagged_structlit_payload_run.c b/test/wcc/938_tagged_structlit_payload_run.c index eecb65cb..43c0f970 100644 --- a/test/wcc/938_tagged_structlit_payload_run.c +++ b/test/wcc/938_tagged_structlit_payload_run.c @@ -433,6 +433,102 @@ static const struct row rows[] = { * after void, arg widen, the bool-leading skip, and the signed * (void|i64) variant (ken hB3); the cast form rides alongside as * the no-drift control (it was always right). */ + /* #38a: chained-dot TAGGED leaf — the dot-spine walkers (value + * chain + ptr chain, both stages) had no TY_TAGGED leaf arm, so + * READS pulled one word (tag; as/match consumers read stale DX + * payload, ken x5c want-28-got-20) and ASSIGNS stored the rhs + * over the TAG slot (ken x5d). Six rows: literal-init read, + * assign-then-read, depth-3 chain read-before-write (ken hB1), + * (void|str) 32B box incl. void-over-str write (ken hB2), + * *outer root, mid-chain *rep. The INDEX-spine sibling + * (xs[i].min) is filed separately (task #58) — NOT a row here. */ + { "chained_tagged_literal_read", + "package main;\n" + "type rep = struct { id: size, min: (void | size), name: (void | str) };\n" + "type outer = struct { tag: size, r: rep };\n" + "export fn main() i32 = {\n" + " let o = outer { tag = 4: size, r = rep { id = 6: size, min = 8: size, name = void } };\n" + " if (o.tag != 4) { return 1; };\n" + " if (o.r.id != 6) { return 2; };\n" + " if (!(o.r.min is size)) { return 3; };\n" + " if (o.r.min as size != 8) { return 4; };\n" + " if (!(o.r.name is void)) { return 5; };\n" + " return 0;\n" + "};\n", 0, 0 }, + { "chained_tagged_assign", + "package main;\n" + "type rep = struct { id: size, min: (void | size) };\n" + "type outer = struct { tag: size, r: rep };\n" + "export fn main() i32 = {\n" + " let o: outer = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n" + " o.r.min = 8: size;\n" + " if (!(o.r.min is size)) { return 1; };\n" + " if (o.r.min as size != 8) { return 2; };\n" + " o.r.min = 9;\n" + " if (!(o.r.min is size)) { return 3; };\n" + " if (o.r.min as size != 9) { return 4; };\n" + " return 0;\n" + "};\n", 0, 0 }, + { "chained_tagged_depth2", + "package main;\n" + "type rep = struct { id: size, min: (void | size) };\n" + "type mid = struct { m: size, r: rep };\n" + "type outer = struct { tag: size, w: mid };\n" + "export fn main() i32 = {\n" + " let o = outer { tag = 4: size, w = mid { m = 2: size, r = rep { id = 6: size, min = 5: size } } };\n" + " if (!(o.w.r.min is size)) { return 5; };\n" + " if (o.w.r.min as size != 5) { return 6; };\n" + " o.w.r.min = 8: size;\n" + " if (o.w.m != 2) { return 1; };\n" + " if (o.w.r.id != 6) { return 2; };\n" + " if (!(o.w.r.min is size)) { return 3; };\n" + " if (o.w.r.min as size != 8) { return 4; };\n" + " return 0;\n" + "};\n", 0, 0 }, + { "chained_tagged_strvariant", + "package main;\n" + "type rep = struct { id: size, nm: (void | str) };\n" + "type outer = struct { tag: size, r: rep };\n" + "export fn main() i32 = {\n" + " let o = outer { tag = 4: size, r = rep { id = 6: size, nm = void } };\n" + " o.r.nm = \"hello\";\n" + " if (!(o.r.nm is str)) { return 1; };\n" + " if ((o.r.nm as str).len != 5) { return 2; };\n" + " match (o.r.nm) {\n" + " case let s: str => { if (s.len != 5) { return 3; }; };\n" + " case void => { return 4; };\n" + " };\n" + " o.r.nm = void;\n" + " if (!(o.r.nm is void)) { return 5; };\n" + " if (o.r.id != 6) { return 6; };\n" + " return 0;\n" + "};\n", 0, 0 }, + { "chained_tagged_ptrroot", + "package main;\n" + "type rep = struct { id: size, min: (void | size) };\n" + "type outer = struct { tag: size, r: rep };\n" + "export fn main() i32 = {\n" + " let o = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n" + " let p: *outer = &o;\n" + " p.r.min = 8: size;\n" + " if (p.r.id != 6) { return 1; };\n" + " if (!(p.r.min is size)) { return 2; };\n" + " if (p.r.min as size != 8) { return 3; };\n" + " return 0;\n" + "};\n", 0, 0 }, + { "chained_tagged_ptrmid", + "package main;\n" + "type rep = struct { id: size, min: (void | size) };\n" + "type outer = struct { tag: size, p: *rep };\n" + "export fn main() i32 = {\n" + " let r = rep { id = 6: size, min = void };\n" + " let o = outer { tag = 4: size, p = &r };\n" + " o.p.min = 8: size;\n" + " if (o.p.id != 6) { return 1; };\n" + " if (!(o.p.min is size)) { return 2; };\n" + " if (o.p.min as size != 8) { return 3; };\n" + " return 0;\n" + "};\n", 0, 0 }, { "untyped_int_bare_widen", "package main;\n" "fn take(v: (void | size)) i32 = {\n"