From 58e6d349a23017ba6008a1ade613e46ada6bf877 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 19 May 2026 17:45:13 +0900 Subject: [PATCH] cmd/w6c/cgen+test: skip dead TRYPROP propret on same-shape ? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per CLAUDE.md rule 10, align cstage down to wwstage — when every variant in a `?` propagation maps to itself, the remap loop emits zero JMPs and the propret label is dead. Lazy-allocate it so the label-counter ID is only consumed when at least one JMP fires. Smoke test selfhost/test/trypromote.ww exercises same-shape (i64|nomem)→(i64|nomem) propagation; cstage and wwstage now emit byte-identical asm for the TRYPROP region. --- .gitignore | 1 + cmd/w6c/cgen.c | 11 ++++++-- selfhost/test/trypromote.ww | 54 +++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 selfhost/test/trypromote.ww diff --git a/.gitignore b/.gitignore index 44adba31..1031ee68 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ /loop /sym_link /memiotest +/trypromote # Per-module build artifacts. The .combined.ww files under selfhost/ # are intentionally tracked — they're frozen bootstrap inputs. diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index b0dd259d..500eb0c4 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -5038,7 +5038,11 @@ cgexpr(Cg *c, Node *n, Local *locals) ins2(c, A_CMPQ, aimm(s_tag), areg(D_AX)); ins1(c, A_JE, abranch(cont)); if (u && r && r->kind == TY_TAGGED && u->params) { - char *propret = mklabel(c, "tryprop_ret"); + /* Same-shape unions remap every variant to itself, so + * the loop emits no JMPs. Skip propret entirely then — + * wwstage doesn't emit a dead label either (CLAUDE.md + * rule 10, task #18). */ + char *propret = NULL; int i = 0; for (Tparam *p = u->params; p; p = p->next, i++) { if (!cg_variant_is_error(u, i)) continue; @@ -5049,10 +5053,13 @@ cgexpr(Cg *c, Node *n, Local *locals) ins2(c, A_CMPQ, aimm(i), areg(D_AX)); ins1(c, A_JNE, abranch(skip)); ins2(c, A_MOVQ, aimm(j), areg(D_AX)); + if (propret == NULL) + propret = mklabel(c, "tryprop_ret"); ins1(c, A_JMP, abranch(propret)); label(c, skip); } - label(c, propret); + if (propret != NULL) + label(c, propret); } ins2(c, A_MOVQ, areg(D_BP), areg(D_SP)); ins1(c, A_POPQ, areg(D_BP)); diff --git a/selfhost/test/trypromote.ww b/selfhost/test/trypromote.ww new file mode 100644 index 00000000..7f7f8fa7 --- /dev/null +++ b/selfhost/test/trypromote.ww @@ -0,0 +1,54 @@ +// selfhost/test/trypromote.ww — smoke for `?` propagation through a +// `!void`-shaped alias. +// +// Task #2 (ww-strings-redesign): proves cgen's TRYPROP tag-remap works +// for the exact pattern lib/errors + os.alloc are about to lean on. We +// cannot smoke this against lib/ today because there are zero lib-side +// `?` users on a `!void` alias yet. Mirrors the shlex.syntaxerr shape +// (lib/shlex/shlex.ww:112) for the "nomem" stub. +// +// Not table-driven on purpose: same-shape `?` is a single cgen emit +// pattern, so varying the operand exercises the same asm. The two +// match arms below cover both runtime outcomes (success unwrap, error +// propagation); asm-level regressions of task #18 are gated by the +// byte-identity tests (994_w6c_ww, 995_self_rebuild). + +package test; + +import fmt; + +type nomem = !void; + +fn stub(fail: i64) (i64 | nomem) = { + if (fail != 0i64) { let e: nomem; return e; }; + return 42i64; +}; + +fn caller(fail: i64) (i64 | nomem) = { + let v = stub(fail)?; + return v + 1i64; +}; + +export fn main() i32 = { + let rc: i32 = 0; + match (caller(0i64)) { + case let n: i64 => { + fmt.println("ok ", n); + if (n != 43i64) { rc = 1; }; + }; + case nomem => { + fmt.println("unexpected nomem on ok path"); + rc = 2; + }; + }; + match (caller(1i64)) { + case let n: i64 => { + fmt.println("unexpected ", n, " on err path"); + rc = 3; + }; + case nomem => { + fmt.println("nomem as expected"); + }; + }; + return rc; +};