wcc: Hare-style enum [storage] { ... } type

`type Foo = enum [intT] { NAME [= expr], ... };`. Storage defaults
to i32; members auto-increment from 0 (or last+1) when `= expr` is
omitted, and value expressions can reference earlier siblings —
enough surface for io::mode-style flag enums (`RDWR = READ | WRITE`).

`Foo.MEMBER` folds to an N_INTLIT in the checker, typed as the
named enum. Binops on enum values yield the same enum (type_eq on
the named pointer), so `mode.R | mode.W` is a `mode`. Enum ↔ int
is a reinterpret-only `as` cast — same register, no tag wrap — so
`mode.RDWR as i32` and `1 as mode` both work without runtime ops.

`is`/`?`/`!` are still tagged-union-only. CSP runtime (chan/proc)
is unchanged; only the type-system slot is touched here.
This commit is contained in:
2026-05-12 04:15:31 +09:00
parent 22999cd3fa
commit 34817eedcd
10 changed files with 257 additions and 1 deletions

View File

@@ -2175,6 +2175,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
Type *st = s ? s->type : NULL;
Type *u = (st && st->kind == TY_NAMED) ? st->under : st;
Type *vt = n->type;
/* Enum ↔ integer: reinterpret-only. The value already lives
* in AX after evaluating the LHS; no tag/unwrap needed. */
{
Type *vu = (vt && vt->kind == TY_NAMED) ? vt->under : vt;
if ((u && u->kind == TY_ENUM) ||
(vu && vu->kind == TY_ENUM)) {
cgexpr(c, s, locals);
break;
}
}
int slot_size = (u && u->kind == TY_TAGGED) ? (int)u->size : 16;
int sl_off = 0;
if (s && s->kind == N_IDENT && s->str) {