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:
@@ -232,6 +232,34 @@ parsetype(Parser *p)
|
||||
n->lhs = parsetype(p);
|
||||
return n;
|
||||
}
|
||||
case TK_ENUM: {
|
||||
/* `enum [storage] { NAME [= expr] [, ...] }`
|
||||
* storage defaults to i32 (lhs == NULL).
|
||||
* Each member is N_TENUMMEMBER with str=name and
|
||||
* lhs=value-expr (NULL means auto-increment from previous). */
|
||||
advance(p);
|
||||
Node *n = newnode(p->a, N_TENUM, pp);
|
||||
if (p->cur.kind != TK_LBRACE)
|
||||
n->lhs = parsetype(p);
|
||||
expect(p, TK_LBRACE);
|
||||
Node *head = NULL, *tail = NULL;
|
||||
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
||||
Pos mp = p->cur.pos;
|
||||
Node *m = newnode(p->a, N_TENUMMEMBER, mp);
|
||||
m->str = expectident(p);
|
||||
m->strlen = strlen(m->str);
|
||||
if (accept(p, TK_ASSIGN))
|
||||
m->lhs = parseexpr(p);
|
||||
if (head == NULL) head = m;
|
||||
else tail->next = m;
|
||||
tail = m;
|
||||
if (!accept(p, TK_COMMA))
|
||||
break;
|
||||
}
|
||||
expect(p, TK_RBRACE);
|
||||
n->list = head;
|
||||
return n;
|
||||
}
|
||||
case TK_VOID: {
|
||||
/* `void` keyword in type-expr context. Synthesise an
|
||||
* N_TNAME so type-resolution treats it like any other
|
||||
|
||||
Reference in New Issue
Block a user