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:
@@ -2175,6 +2175,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
Type *st = s ? s->type : NULL;
|
Type *st = s ? s->type : NULL;
|
||||||
Type *u = (st && st->kind == TY_NAMED) ? st->under : st;
|
Type *u = (st && st->kind == TY_NAMED) ? st->under : st;
|
||||||
Type *vt = n->type;
|
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 slot_size = (u && u->kind == TY_TAGGED) ? (int)u->size : 16;
|
||||||
int sl_off = 0;
|
int sl_off = 0;
|
||||||
if (s && s->kind == N_IDENT && s->str) {
|
if (s && s->kind == N_IDENT && s->str) {
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ nkname(Nkind k)
|
|||||||
case N_VOIDLIT: return "voidlit";
|
case N_VOIDLIT: return "voidlit";
|
||||||
case N_TBANG: return "tbang";
|
case N_TBANG: return "tbang";
|
||||||
case N_YIELD: return "yield";
|
case N_YIELD: return "yield";
|
||||||
|
case N_TENUM: return "tenum";
|
||||||
|
case N_TENUMMEMBER: return "tenummember";
|
||||||
case N_LAST: return "last";
|
case N_LAST: return "last";
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
@@ -158,6 +160,7 @@ pr(FILE *f, Node *n, int d)
|
|||||||
case N_LET:
|
case N_LET:
|
||||||
case N_TNAME:
|
case N_TNAME:
|
||||||
case N_TFIELD:
|
case N_TFIELD:
|
||||||
|
case N_TENUMMEMBER:
|
||||||
case N_FIELD:
|
case N_FIELD:
|
||||||
case N_ATTR:
|
case N_ATTR:
|
||||||
if (n->str) { fputc(' ', f); printq(f, n->str); }
|
if (n->str) { fputc(' ', f); printq(f, n->str); }
|
||||||
|
|||||||
163
cmd/wcc/check.c
163
cmd/wcc/check.c
@@ -164,6 +164,85 @@ tagged_success_type(Type *u)
|
|||||||
return u->params ? u->params->type : NULL;
|
return u->params ? u->params->type : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eval_enum_value — fold an enum member-value expression to a u64
|
||||||
|
* constant. Sees prior siblings via the `prev` Tfield list (each
|
||||||
|
* carries the member's name and resolved value in .offset). Returns
|
||||||
|
* 1 on success; on failure emits the error and returns 0. The op set
|
||||||
|
* is the constant subset typical of Hare-style flag enums:
|
||||||
|
* literal, sibling-ident, + - * / % & | ^ << >>, unary - and ~. */
|
||||||
|
static int
|
||||||
|
eval_enum_value(Checker *c, Node *n, Tfield *prev, u64 *out)
|
||||||
|
{
|
||||||
|
if (n == NULL) return 0;
|
||||||
|
switch (n->kind) {
|
||||||
|
case N_INTLIT:
|
||||||
|
case N_RUNELIT:
|
||||||
|
*out = n->uval;
|
||||||
|
return 1;
|
||||||
|
case N_TRUE: *out = 1; return 1;
|
||||||
|
case N_FALSE: *out = 0; return 1;
|
||||||
|
case N_IDENT: {
|
||||||
|
for (Tfield *f = prev; f; f = f->next) {
|
||||||
|
if (f->name && n->str &&
|
||||||
|
strcmp(f->name, n->str) == 0) {
|
||||||
|
*out = f->offset;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err(c, n->pos, "enum value: unknown identifier '%s'",
|
||||||
|
n->str ? n->str : "?");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case N_BIN: {
|
||||||
|
u64 a, b;
|
||||||
|
if (!eval_enum_value(c, n->lhs, prev, &a) ||
|
||||||
|
!eval_enum_value(c, n->rhs, prev, &b))
|
||||||
|
return 0;
|
||||||
|
switch (n->op) {
|
||||||
|
case TK_PLUS: *out = a + b; return 1;
|
||||||
|
case TK_MINUS: *out = a - b; return 1;
|
||||||
|
case TK_STAR: *out = a * b; return 1;
|
||||||
|
case TK_SLASH:
|
||||||
|
if (b == 0) goto divzero;
|
||||||
|
*out = a / b; return 1;
|
||||||
|
case TK_PERCENT:
|
||||||
|
if (b == 0) goto divzero;
|
||||||
|
*out = a % b; return 1;
|
||||||
|
case TK_AMP: *out = a & b; return 1;
|
||||||
|
case TK_PIPE: *out = a | b; return 1;
|
||||||
|
case TK_CARET: *out = a ^ b; return 1;
|
||||||
|
case TK_LSHIFT: *out = a << b; return 1;
|
||||||
|
case TK_RSHIFT: *out = a >> b; return 1;
|
||||||
|
default:
|
||||||
|
err(c, n->pos, "enum value: unsupported binary op %s",
|
||||||
|
tokname(n->op));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
divzero:
|
||||||
|
err(c, n->pos, "enum value: division by zero");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case N_UN: {
|
||||||
|
u64 v;
|
||||||
|
if (!eval_enum_value(c, n->lhs, prev, &v))
|
||||||
|
return 0;
|
||||||
|
switch (n->op) {
|
||||||
|
case TK_MINUS: *out = (u64)(-(i64)v); return 1;
|
||||||
|
case TK_TILDE: *out = ~v; return 1;
|
||||||
|
case TK_PLUS: *out = v; return 1;
|
||||||
|
default:
|
||||||
|
err(c, n->pos, "enum value: unsupported unary op %s",
|
||||||
|
tokname(n->op));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err(c, n->pos,
|
||||||
|
"enum value must be a constant integer expression");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Type *
|
static Type *
|
||||||
resolve_type(Checker *c, Node *n)
|
resolve_type(Checker *c, Node *n)
|
||||||
{
|
{
|
||||||
@@ -387,6 +466,50 @@ resolve_type(Checker *c, Node *n)
|
|||||||
t->size = (off + maxalign - 1) & ~(maxalign - 1);
|
t->size = (off + maxalign - 1) & ~(maxalign - 1);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
case N_TENUM: {
|
||||||
|
Type *t = newtype(c->a, TY_ENUM);
|
||||||
|
Type *storage = ty_i32; /* default storage */
|
||||||
|
if (n->lhs) {
|
||||||
|
Type *s = resolve_type(c, n->lhs);
|
||||||
|
if (s == ty_err || !type_isint(s))
|
||||||
|
err(c, n->lhs->pos,
|
||||||
|
"enum storage type must be integer");
|
||||||
|
else
|
||||||
|
storage = s;
|
||||||
|
}
|
||||||
|
t->sub = storage;
|
||||||
|
t->size = storage->size;
|
||||||
|
t->align = storage->align;
|
||||||
|
Tfield *head = NULL, *tail = NULL;
|
||||||
|
u64 prev = (u64)-1; /* so first omitted → 0 */
|
||||||
|
for (Node *m = n->list; m; m = m->next) {
|
||||||
|
u64 val;
|
||||||
|
if (m->lhs == NULL) {
|
||||||
|
val = prev + 1;
|
||||||
|
} else if (!eval_enum_value(c, m->lhs, head, &val)) {
|
||||||
|
val = prev + 1;
|
||||||
|
}
|
||||||
|
prev = val;
|
||||||
|
for (Tfield *e = head; e; e = e->next) {
|
||||||
|
if (e->name && m->str &&
|
||||||
|
strcmp(e->name, m->str) == 0) {
|
||||||
|
err(c, m->pos,
|
||||||
|
"duplicate enum member '%s'",
|
||||||
|
m->str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tfield *tf = amalloc(c->a, sizeof *tf);
|
||||||
|
tf->name = m->str;
|
||||||
|
tf->type = NULL;
|
||||||
|
tf->offset = val;
|
||||||
|
if (head == NULL) head = tf;
|
||||||
|
else tail->next = tf;
|
||||||
|
tail = tf;
|
||||||
|
}
|
||||||
|
t->fields = head;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return err(c, n->pos, "expected type expression");
|
return err(c, n->pos, "expected type expression");
|
||||||
}
|
}
|
||||||
@@ -555,6 +678,34 @@ cexpr(Checker *c, Node *n)
|
|||||||
* missing. */
|
* missing. */
|
||||||
return n->type = ty_err;
|
return n->type = ty_err;
|
||||||
}
|
}
|
||||||
|
/* enum member access: TypeName.MEMBER → fold to
|
||||||
|
* the member's integer literal value. Type is the
|
||||||
|
* (named) enum type itself, so bitwise ops between
|
||||||
|
* members yield the same enum type via type_eq. */
|
||||||
|
if (ms && ms->kind == SK_TYPE && ms->type) {
|
||||||
|
Type *u = (ms->type->kind == TY_NAMED)
|
||||||
|
? ms->type->under : ms->type;
|
||||||
|
if (u && u->kind == TY_ENUM) {
|
||||||
|
for (Tfield *f = u->fields; f; f = f->next) {
|
||||||
|
if (f->name && n->str &&
|
||||||
|
strcmp(f->name, n->str) == 0) {
|
||||||
|
n->kind = N_INTLIT;
|
||||||
|
n->uval = f->offset;
|
||||||
|
n->str = aprintf(c->a, "%llu",
|
||||||
|
(unsigned long long)f->offset);
|
||||||
|
n->strlen = strlen(n->str);
|
||||||
|
n->lhs = NULL;
|
||||||
|
n->rhs = NULL;
|
||||||
|
n->tsuffix = NULL;
|
||||||
|
return n->type = ms->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n->type = err(c, n->pos,
|
||||||
|
"no enum member '%s' in %s",
|
||||||
|
n->str ? n->str : "?",
|
||||||
|
ms->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Type *base = cexpr(c, n->lhs);
|
Type *base = cexpr(c, n->lhs);
|
||||||
if (base == NULL || base == ty_err) return n->type = ty_err;
|
if (base == NULL || base == ty_err) return n->type = ty_err;
|
||||||
@@ -991,6 +1142,18 @@ cexpr(Checker *c, Node *n)
|
|||||||
* itself (as). */
|
* itself (as). */
|
||||||
if (n->rhs) n->rhs->type = vt;
|
if (n->rhs) n->rhs->type = vt;
|
||||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||||
|
/* Enum ↔ integer cast: `enumval as intT` or `int as enumT`.
|
||||||
|
* Reinterpret-only — the storage shape is already integer, so
|
||||||
|
* cgen treats the cast as a no-op (the value lives in the same
|
||||||
|
* register). The `is` form is rejected; enums aren't sums. */
|
||||||
|
Type *uu = u;
|
||||||
|
Type *vu = (vt && vt->kind == TY_NAMED) ? vt->under : vt;
|
||||||
|
int lhs_enum = uu && uu->kind == TY_ENUM;
|
||||||
|
int rhs_enum = vu && vu->kind == TY_ENUM;
|
||||||
|
if (n->kind == N_TYPEASSERT && (lhs_enum || rhs_enum) &&
|
||||||
|
type_isint(t) && type_isint(vt)) {
|
||||||
|
return n->type = vt;
|
||||||
|
}
|
||||||
if (u == NULL || u->kind != TY_TAGGED) {
|
if (u == NULL || u->kind != TY_TAGGED) {
|
||||||
const char *op = (n->kind == N_TYPETEST) ? "is" : "as";
|
const char *op = (n->kind == N_TYPETEST) ? "is" : "as";
|
||||||
return n->type = err(c, n->pos,
|
return n->type = err(c, n->pos,
|
||||||
|
|||||||
@@ -232,6 +232,34 @@ parsetype(Parser *p)
|
|||||||
n->lhs = parsetype(p);
|
n->lhs = parsetype(p);
|
||||||
return n;
|
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: {
|
case TK_VOID: {
|
||||||
/* `void` keyword in type-expr context. Synthesise an
|
/* `void` keyword in type-expr context. Synthesise an
|
||||||
* N_TNAME so type-resolution treats it like any other
|
* N_TNAME so type-resolution treats it like any other
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ static const struct kwent kwtab[] = {
|
|||||||
{ "def", TK_DEF },
|
{ "def", TK_DEF },
|
||||||
{ "defer", TK_DEFER },
|
{ "defer", TK_DEFER },
|
||||||
{ "else", TK_ELSE },
|
{ "else", TK_ELSE },
|
||||||
|
{ "enum", TK_ENUM },
|
||||||
{ "export", TK_EXPORT },
|
{ "export", TK_EXPORT },
|
||||||
{ "false", TK_FALSE },
|
{ "false", TK_FALSE },
|
||||||
{ "fn", TK_FN },
|
{ "fn", TK_FN },
|
||||||
@@ -99,6 +100,7 @@ tokname(Tkind k)
|
|||||||
case TK_MATCH: return "match";
|
case TK_MATCH: return "match";
|
||||||
case TK_CONST: return "const";
|
case TK_CONST: return "const";
|
||||||
case TK_UNDER: return "_";
|
case TK_UNDER: return "_";
|
||||||
|
case TK_ENUM: return "enum";
|
||||||
|
|
||||||
case TK_LPAREN: return "(";
|
case TK_LPAREN: return "(";
|
||||||
case TK_RPAREN: return ")";
|
case TK_RPAREN: return ")";
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ type_isint(Type *t)
|
|||||||
case TY_UNTYPED_INT:
|
case TY_UNTYPED_INT:
|
||||||
case TY_UNTYPED_RUNE:
|
case TY_UNTYPED_RUNE:
|
||||||
return 1;
|
return 1;
|
||||||
|
case TY_ENUM: return type_isint(t->sub);
|
||||||
case TY_NAMED: return type_isint(t->under);
|
case TY_NAMED: return type_isint(t->under);
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
@@ -370,6 +371,9 @@ type_name(Arena *a, Type *t)
|
|||||||
}
|
}
|
||||||
return aprintf(a, "(%s)", acc);
|
return aprintf(a, "(%s)", acc);
|
||||||
}
|
}
|
||||||
|
case TY_ENUM:
|
||||||
|
return aprintf(a, "enum %s",
|
||||||
|
t->sub ? type_name(a, t->sub) : "i32");
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|||||||
12
cmd/wcc/ww.h
12
cmd/wcc/ww.h
@@ -178,6 +178,7 @@ typedef enum {
|
|||||||
TK_IS, /* Hare-style type test: e is T */
|
TK_IS, /* Hare-style type test: e is T */
|
||||||
TK_VOID, /* `void` — both a type name and a zero-size value */
|
TK_VOID, /* `void` — both a type name and a zero-size value */
|
||||||
TK_YIELD, /* `yield expr;` — value-return from a match arm */
|
TK_YIELD, /* `yield expr;` — value-return from a match arm */
|
||||||
|
TK_ENUM, /* Hare-style `enum [storage] { ... }` type form */
|
||||||
|
|
||||||
TK_LAST /* sentinel for tables */
|
TK_LAST /* sentinel for tables */
|
||||||
} Tkind;
|
} Tkind;
|
||||||
@@ -300,6 +301,11 @@ typedef enum {
|
|||||||
N_TBANG, /* `!T` — error-flagged type. lhs = inner type. */
|
N_TBANG, /* `!T` — error-flagged type. lhs = inner type. */
|
||||||
N_YIELD, /* `yield expr;` — set the enclosing match's value-
|
N_YIELD, /* `yield expr;` — set the enclosing match's value-
|
||||||
* return and jump to its end label. lhs = value. */
|
* return and jump to its end label. lhs = value. */
|
||||||
|
N_TENUM, /* `enum [storage] { ... }` type form.
|
||||||
|
* lhs = storage type expr or NULL (default i32);
|
||||||
|
* list = chain of N_TENUMMEMBER. */
|
||||||
|
N_TENUMMEMBER, /* enum member. str=name, lhs=value expr or NULL
|
||||||
|
* (auto-increment when omitted). */
|
||||||
|
|
||||||
N_LAST
|
N_LAST
|
||||||
} Nkind;
|
} Nkind;
|
||||||
@@ -377,7 +383,11 @@ typedef enum {
|
|||||||
TY_UNTYPED_STR,
|
TY_UNTYPED_STR,
|
||||||
TY_UNTYPED_RUNE,
|
TY_UNTYPED_RUNE,
|
||||||
TY_UNTYPED_BOOL,
|
TY_UNTYPED_BOOL,
|
||||||
TY_UNTYPED_NIL
|
TY_UNTYPED_NIL,
|
||||||
|
/* Appended at the tail to keep existing TY_* values stable —
|
||||||
|
* lib/ww/typ.ww mirrors them as explicit `def` numbers. */
|
||||||
|
TY_ENUM /* `enum [storage] { ... }`. sub=storage,
|
||||||
|
* fields=member list (Tfield.offset = u64 value). */
|
||||||
} TypeKind;
|
} TypeKind;
|
||||||
|
|
||||||
typedef struct Tfield Tfield;
|
typedef struct Tfield Tfield;
|
||||||
|
|||||||
@@ -167,6 +167,11 @@ main(void)
|
|||||||
"fn ti(r: (i64 | i32)) bool = { return r is i64; };",
|
"fn ti(r: (i64 | i32)) bool = { return r is i64; };",
|
||||||
"fn ai(r: (i64 | i32)) i64 = { return r as i64; };",
|
"fn ai(r: (i64 | i32)) i64 = { return r as i64; };",
|
||||||
"fn br(r: (i64 | str)) i32 = { if (r is i64) { return 1; }; return 0; };",
|
"fn br(r: (i64 | str)) i32 = { if (r is i64) { return 1; }; return 0; };",
|
||||||
|
|
||||||
|
/* Hare-style enum types */
|
||||||
|
"type color = enum { RED, GREEN, BLUE, };",
|
||||||
|
"type mode = enum u8 { NONE = 0, READ = 1, WRITE = 2, RDWR = READ | WRITE };",
|
||||||
|
"type whence = enum i32 { SET = 0, CUR, END };",
|
||||||
};
|
};
|
||||||
int n = sizeof parses / sizeof parses[0];
|
int n = sizeof parses / sizeof parses[0];
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
|
|||||||
@@ -162,6 +162,23 @@ static const struct row rows[] = {
|
|||||||
"case str | f64 => return 9; case let b: bool => return 1; }; "
|
"case str | f64 => return 9; case let b: bool => return 1; }; "
|
||||||
"return 0; };",
|
"return 0; };",
|
||||||
"case: f64 is not a variant" },
|
"case: f64 is not a variant" },
|
||||||
|
|
||||||
|
/* enum types */
|
||||||
|
{ "type color = enum { RED, GREEN, BLUE };\n"
|
||||||
|
"fn f() i32 = { return color.GREEN as i32; };", "ok" },
|
||||||
|
{ "type mode = enum u8 { R = 1, W = 2, RW = R | W };\n"
|
||||||
|
"fn f() i32 = { let m: mode = mode.RW; return m as i32; };", "ok" },
|
||||||
|
{ "type mode = enum u8 { R = 1, W = 2 };\n"
|
||||||
|
"fn f() i32 = { return (mode.R | mode.W) as i32; };", "ok" },
|
||||||
|
{ "type c = enum { A, B };\n"
|
||||||
|
"fn f() i32 = { return c.NOPE as i32; };",
|
||||||
|
"no enum member 'NOPE'" },
|
||||||
|
{ "type c = enum { A, A };\n"
|
||||||
|
"fn f() i32 = { return c.A as i32; };",
|
||||||
|
"duplicate enum member" },
|
||||||
|
{ "type c = enum bool { A };\n"
|
||||||
|
"fn f() i32 = { return c.A as i32; };",
|
||||||
|
"enum storage type must be integer" },
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
@@ -1086,6 +1086,20 @@ static const struct row rows[] = {
|
|||||||
" let e: str = r as str;\n"
|
" let e: str = r as str;\n"
|
||||||
" return e.len: i32;\n"
|
" return e.len: i32;\n"
|
||||||
"};", 3 },
|
"};", 3 },
|
||||||
|
/* enum: auto-increment, explicit value, sibling-ref */
|
||||||
|
{ "type color = enum { RED, GREEN, BLUE };\n"
|
||||||
|
"fn main() i32 = { return color.BLUE as i32; };", 2 },
|
||||||
|
{ "type mode = enum u8 { R = 1, W = 2, RW = R | W };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let m: mode = mode.RW;\n"
|
||||||
|
" return m as i32;\n"
|
||||||
|
"};", 3 },
|
||||||
|
/* enum: bitwise op between two members yields the same enum type */
|
||||||
|
{ "type mode = enum u8 { R = 1, W = 2 };\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
" let m: mode = mode.R | mode.W;\n"
|
||||||
|
" return m as i32;\n"
|
||||||
|
"};", 3 },
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user