wcc/cgen: enum as int is a value conversion, not a tagged assertion (ww stage)

The value-passthrough arm gated on node shape (isenumexpr); a
constant-folded enum member reaches cgen as an int-literal node,
missed the arm, and fell into the tagged-union assertion path
(CMPQ $0 + JNE -> exit 1) — silent wrong code for every `enum as
int`, fnmatch's flag tests included. Gate on the stamped operand
type (tichase == TY_ENUM) at the cgtypeassert choke point, mirroring
cstage cgen.c N_TYPEASSERT; dead node-shape helpers deleted. Tagged
`as` assertion path byte-id-unmoved (control rows). Six @test pins
in attest_pass.ww, dual-stage. (#27b-team, cat-A)
This commit is contained in:
2026-06-11 02:37:32 +09:00
parent 19e9535ef4
commit 00b9933ee0
4 changed files with 111 additions and 150 deletions

View File

@@ -6,6 +6,12 @@ import rt; // #3/B': check_empty_alloc_annotated calls alloc → rt_malloc.
type point = struct { x: i32, y: i32 };
// #27b: enum bases for the `<enum> as <inttype>` value-reinterpret pins
// below (multiple enum bases × int widths).
type eflag = enum i32 { A = 1, B = 2, C = 4 };
type ebyte = enum u8 { LO = 1, HI = 200 };
type eplain = enum { Z = 0, ONE = 1, TWO = 2 };
// #87: a PLAIN (non-alias) module-level tagged-union global SEGV'd BOTH
// stages — no static DATA emitted (read as a frame-local at BP) + the
// match scrutinee read saved BP as the tag. Fix: emit the 32B box that
@@ -176,3 +182,48 @@ let g86_tup: (u32, u32) = (1u32, 2u32);
if (g86_tup.0 != l.0) { let _: i32 = 1 / 0; };
if (g86_tup.1 != l.1) { let _: i32 = 1 / 0; };
};
// #27b: `<enum> as <inttype>` must lower as a value reinterpret (the
// integer storage already occupies the register), NOT a tagged-union
// assertion. Pre-fix wwstage gated the passthrough on NODE SHAPE
// (isenumexpr), so a constant-folded enum MEMBER (`eflag.B`, an int-
// literal node carrying the enum type_) missed the arm and fell into the
// tag-assert + exit(1) path — every fnmatch `(fl as i32)` flag test
// aborted (8/8 → 0/8 on the wwstage build). Fix gates on the stamped
// operand/result TYPE (mirror cstage cmd/w6c/cgen.c N_TYPEASSERT). These
// rows pin the construct by construction: member-as (the folded shape),
// ident-as (the param shape), the fnmatch flag-test combination, int→enum
// (result-type gate), and multiple enum bases × int widths.
@test fn check_enum_as_member() void = {
if ((eflag.B as i32) != 2) { let _: i32 = 1 / 0; };
if ((eflag.C as i32) != 4) { let _: i32 = 1 / 0; };
};
@test fn check_enum_as_flagtest() void = {
let fl: eflag = eflag.A; // 1 & 2 == 0
if (((fl as i32) & (eflag.B as i32)) != 0) { let _: i32 = 1 / 0; };
let fl2: eflag = eflag.C; // 4 & 4 != 0
if (((fl2 as i32) & (eflag.C as i32)) == 0) { let _: i32 = 1 / 0; };
};
@test fn check_enum_as_widths() void = {
if ((ebyte.HI as u8) != 200) { let _: i32 = 1 / 0; };
if ((ebyte.HI as i32) != 200) { let _: i32 = 1 / 0; };
if ((eplain.TWO as int) != 2) { let _: i32 = 1 / 0; };
if ((eflag.B as i64) != 2i64) { let _: i32 = 1 / 0; };
};
@test fn check_int_as_enum() void = {
let v: i32 = 2i32;
let e: eflag = v as eflag; // result-type enum gate
if ((e as i32) != 2) { let _: i32 = 1 / 0; };
};
// CONTROL (#27b): a legit tagged-union `as` must KEEP the tag-assert +
// payload-unwrap lowering — byte-id UNMOVED by the enum-passthrough fix.
// This is the path enum-as must not hijack and must not be hijacked by.
@test fn check_tagged_as_control() void = {
let b: (i32 | str) = 7;
let v: i32 = b as i32;
if (v != 7) { let _: i32 = 1 / 0; };
};