diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index ba27a00d..08d4c239 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1236; +def corpuscount: i32 = 1237; def errorcount: i32 = 314; def compilecount: i32 = 12; -def runcount: i32 = 144; +def runcount: i32 = 145; def runexitcount: i32 = 766; -def nativecount: i32 = 2472; -def corpushash: str = "4c8f4a693d2d8193b15f4215610c1d070dc83493b1189af772ca09cad4a69db7"; +def nativecount: i32 = 2474; +def corpushash: str = "7763f2eb34524f67355114f5bdfbf3f64415152a8ec64060fe32a322bdbe1b24"; type directive = enum i32 { ERROR = 0, diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 8cb6957f..3ebb6726 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -3328,6 +3328,22 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = { }; if (k == syntax.nkind.N_BIN) { let tn: *syntax.node = binoptype(c, e); + // #59.9: checkisas pre-stamps an enum OR-fold (`(m.A|m.B) as + // u32`) TY_ENUM and folds the member N_DOTs to int literals; + // this post-order revisit re-derives from those now-untyped + // literals and clobbered the stamp, so cgtypeassert's #27b + // reinterpret gate missed and emitted a phantom tagged assert + // (unconditional exit 1). Keep an existing enum stamp; the + // returned tnode contract for callers is unchanged. + let pre599: *syntax.tinfo = e.type_: *syntax.tinfo; + if (pre599 != nil) { + let prech: *syntax.tinfo = tichase(pre599); + if (prech != nil) { + if (prech.kind == syntax.tykind.TY_ENUM) { + return tn; + }; + }; + }; e.type_ = tinfofornode(c, tn): *void; return tn; }; @@ -6353,6 +6369,15 @@ fn checkisas(c: *checker, n: *syntax.node) void = { if (n.lhs.kind == syntax.nkind.N_TRYPROP || n.lhs.kind == syntax.nkind.N_TRYUNW) { st = exprtype(c, n.lhs, nil); }; + // #59.9: an N_BIN operand (`(m.A | m.B) as u32`) was never + // typed, so the OR-fold reached cgen unstamped and + // cgtypeassert's enum-reinterpret gate (#27b) missed — + // lowering a plain enum value as a phantom tagged assert + // (unconditional exit 1). cstage types the lhs + // unconditionally (cmd/wcc/check.c:2220 cexpr(c, n->lhs)). + if (n.lhs.kind == syntax.nkind.N_BIN) { + st = exprtype(c, n.lhs, nil); + }; }; let u: *syntax.node = resolvealias(c, unwrapbang(st)); if (u == nil) { return; }; diff --git a/test/wcc/989_lib_byteid.c b/test/wcc/989_lib_byteid.c index 12e7fe09..642f16ef 100644 --- a/test/wcc/989_lib_byteid.c +++ b/test/wcc/989_lib_byteid.c @@ -169,8 +169,13 @@ static const struct ent ents[] = { /* #59.7 siphash graduated to M_ID above (#61 fix) */ { .fixture = "lib/log/logtest.ww", .mode = M_DIVERGE, .cite = "#59.8" }, + /* #59.9 graduated: the wwstage checker never typed an N_BIN operand + * of `as`, so an enum OR-fold reached cgen unstamped and lowered as + * a phantom tagged assert (unconditional exit 1). checkisas now + * types the N_BIN lhs and the post-order restamp preserves the enum + * stamp. Runtime pin: r599_enum_orfold_as. */ { .fixture = "lib/os/stattest.ww", - .mode = M_DIVERGE, .cite = "#59.9" }, + .mode = M_ID, .cite = "#59.9 graduated by the checkisas N_BIN stamp" }, /* #59.10 stoftest graduated to M_ID above (#62 fix) */ { .fixture = "lib/ww/syntax/toktest.ww", .inc = "lib/ww", .mode = M_ID, .cite = "#59.11 graduated by #146 str==" }, diff --git a/test/wcc/data/r599_enum_orfold_as/case.ww b/test/wcc/data/r599_enum_orfold_as/case.ww new file mode 100644 index 00000000..f795f275 --- /dev/null +++ b/test/wcc/data/r599_enum_orfold_as/case.ww @@ -0,0 +1,16 @@ +//ww:run +// #59.9: an enum member OR-fold under `as`. The pre-fix wwstage +// checker never typed the N_BIN operand, so cgtypeassert's enum +// reinterpret gate missed and lowered a phantom tagged assert that +// unconditionally exited 1. +package main; + +type mask = enum u32 { A = 1, B = 2, C = 4 }; + +fn main() i32 = { + let w: u32 = (mask.A | mask.B | mask.C) as u32; + if (w != 7u32) { return 1; }; + let v: u32 = (mask.A | mask.C) as u32; + if (v != 5u32) { return 2; }; + return 0; +};