

# `enum` Types

An `enum` defines a *tagged union* type: a value that is exactly one of several
named variants, optionally carrying a payload.

Use enums to model:

- finite state machines (connection state, parser state),
- protocol messages and events,
- and any API where “exactly one of these cases” is the core invariant.

If your goal is “a function can fail with one of several error shapes”, prefer
typed errors ([typed errors](/silk/docs/language/typed-errors/)) over enums.

## Notes

What works end-to-end today (parser → checker → lowering → codegen):

- **Enum declarations** with:
 - unit variants (`A`),
 - tuple variants (`Data(int)` and `Pair(int, int)`),
 - and an optional trailing comma after the last variant.
- **Construction**:
 - unit variants as values: `E::A`,
 - tuple variants as calls: `E::Data(7)`.
 - type-directed shorthand:
 - when the expected type is an enum `E`, unit variants may be written as `A`
 (sugar for `E::A`),
 - when the expected type is an enum `E`, tuple variants may be written as
 `Data(7)` (sugar for `E::Data(7)`).
- **Function signatures**:
 - enums may be used in parameter lists and return types,
 - error-producing functions may return enums (`-> E | ErrorType...`), and `call()?` works when the success type is an enum.
- **[`match`](/silk/wiki/language/flow-match/) expression over enums**:
 - patterns are restricted to enum variants (`E::A`, `E::Data(x)`) and may
 use shorthand (`A`, `Data(x)`) when the scrutinee type is the enum,
 - binders may be names or `_`,
 - no guards (`if ...`) yet,
 - and expression-form matches must still be *exhaustive* in the current
 subset:
 - either by explicit arm coverage for every variant,
 - or by a single final wildcard `_` arm that covers all remaining
 unmatched variants.
- **`??` over ordinary two-variant enums**:
 - for a named enum with exactly two declared variants, `value ?? fallback`
 treats the first declared variant as the “success” arm,
 - if that first variant is unit, the expression yields that enum value,
 - if that first variant carries exactly one payload, the expression yields
 that payload,
 - and if the enum value is the second declared variant, the fallback
 expression is evaluated.
- **[`match`](/silk/wiki/language/flow-match/) statement over ordinary enum values**:
 - enum variant arms must use the qualified form `E::Variant(...)`,
 - bare identifiers remain reserved for binder-style arms,
 - and both expression-form and statement-form ordinary enum matches now allow
 one final `_` catch-all arm, which must cover at least one remaining
 variant.
- **Generic enums (monomorphized)**:
 - `enum Name(T, ...) { ... }` declarations are supported in module-set builds
 that run monomorphization,
 - instantiated enums behave like ordinary enums once referenced (including
 construction and [`match`](/silk/wiki/language/flow-match/)),
 - callers typically introduce a local type alias for the instantiated enum
 (for example `type R = Result(int, string);`) and then use `R::Ok(...)` /
 `R::Err(...)` as constructors and patterns.
- **`impl` blocks on enums**:
 - enums may have `impl` blocks (including generic `impl EnumName(T, ...)`),
 - static methods are callable as `EnumName.method(...)` (including through
 type aliases for instantiated generic enums),
 - instance methods are callable as `value.method(...)` when the first
 parameter is a receiver (`self: &EnumName` / `mut self: &EnumName`),
 - the special `constructor` method used by `new Type(...)` is for `struct`
 types; enums do not support `constructor` methods in the Supported forms.

Not implemented yet (or not yet stable/documented):

- Guards in enum match arms (`E::A if cond => ...`).
- A stable ABI story for passing/returning enums across the C99 boundary (do
 not assume an enum layout until it is specified in [abi libsilk](/silk/docs/compiler/abi-libsilk/)).

When the compiler rejects an enum construct in the Supported forms, the most
common error is `E2002` (“unsupported expression in the Supported forms”). Type
mismatches inside enum constructors or match arms are `E2001` (“type mismatch”).

## Surface Syntax

Enum declarations introduce a nominal type and its variants:

```silk
enum RecvJob {
  Msg(Job),
  Cancelled,
  Timeout,
}
```

Rules:

- Variant names are identifiers and must be unique within the enum.
- Variant names may not be the reserved optional constructors `Some` / `None`.
- An enum must declare at least one variant.
- A variant is either:
 - a **unit** variant (no payload): `Cancelled`,
 - or a **tuple** variant with one or more payload element types: `Msg(Job)`,
 `Pair(int, int)`.
- A trailing comma after the last variant is permitted.

## Construction

### Unit variants

Unit variants are constructed as values using `Enum::Variant` (or, in
type-directed contexts, just `Variant`):

```silk
enum E {
  A,
  B,
}

fn main () -> int {
  let x: E = E::A;
  let y: E = A;
  return 0;
}
```

Notes:

- `E::A()` and `A()` are invalid in the Supported forms (unit variants are not callable).
- A unit variant is an ordinary value of its enum type. It may initialize a
 binding or aggregate field, be returned or passed to a function, and be
 assigned to an existing mutable binding or struct field. These value uses
 accept both the qualified `E::A` form and the type-directed `A` shorthand.

## Two-Variant `??` Shortcut

When a named enum declares exactly two variants, the `??` operator may be used
as a compact success/fallback form.

Rules in the Supported forms:

- declaration order matters: the first declared variant is the “success” case,
 and the second declared variant triggers the fallback,
- if the first variant is unit, `value ?? fallback` yields that enum value,
- if the first variant carries exactly one payload, `value ?? fallback` yields
 that payload,
- if the first variant carries more than one payload element, use [`match`](/silk/wiki/language/flow-match/)
 instead,
- the second variant may be unit or may carry payloads; its payload is ignored
 by the `??` form and the fallback expression runs.
- the right-hand side is still an expression in the current grammar; terminal
 statements such as `return`, `break`, and `continue` are not accepted there
 yet.

Examples:

```silk
enum Ready {
  Go,
  Stop,
}

fn require_ready (r: Ready) -> Ready {
  return r ?? Ready::Go;
}

enum ParsedPort {
  Port(int),
  Invalid,
}

fn port_or_default (p: ParsedPort) -> int {
  return p ?? 80;
}
```

### Tuple variants

Tuple variants are constructed using `Enum::Variant(<args...>)` where the
argument count and types match the variant’s declared payload element types:

```silk
enum E {
  Data(int),
  Pair(int, int),
  Empty,
}

fn main () -> int {
  let a: E = E::Data(7);
  let b: E = Data(7);
  let b: E = E::Pair(1, 2);
  let c: E = E::Empty;
  return 0;
}
```

Notes:

- `E::Data` by itself is not a value in the Supported forms (tuple variants must
 be constructed with `(...)`).
- If a tuple-variant constructor argument has the wrong type, you get `E2001`.
- If the argument count does not match the variant definition, the compiler
 currently rejects the construct with `E2002`.

### Generic enums (instantiation via alias)

When an enum is generic, callers typically alias an instantiation and then use
that alias as the qualifier for constructors:

```silk
enum Result(T, E) {
  Ok(T),
  Err(E),
}

type R = Result(int, int);

fn main () -> int {
  let x: R = R::Ok(123);
  return match x {
    R::Ok(v) => v,
    R::Err(_) => 0,
  };
}
```

### Namespaced enums (packages)

Across packages, enums and variants may be referenced with `::` qualification.
For example, if `util` defines `enum Mode { Inc, Dec }`, an importer can write:

- `util::Mode` as the type name, and
- `util::Mode::Inc` / `util::Mode::Dec` as the constructors and patterns.

See [packages imports exports](/silk/docs/language/packages-imports-exports/) for module-set rules and for how
package imports seed qualified type names.

## Matching

Enum values are typically consumed via [`match`](/silk/wiki/language/flow-match/) expressions. The [`match`](/silk/wiki/language/flow-match/)
expression rules are defined in [flow match](/silk/docs/language/flow-match/); this section
focuses on the enum-specific subset.

### Patterns

Enum patterns are variant patterns:

- Unit variant: `E::A`
- Tuple variant: `E::Data(x)`, `E::Pair(a, b)`
- Tuple binder omission: `E::Pair(_, b)` (underscore binder ignores that element)

Shorthand:

- When the scrutinee type is the enum `E`, the qualifier may be omitted:
 - Unit variant: `A`
 - Tuple variant: `Data(x)`, `Pair(a, b)`

For instantiated generic enums, the qualifier `E` may be a type alias (for
example `type R = Result(int, string);` then `R::Ok(v)` / `R::Err(e)`).

Binders:

- introduce a name scoped to that arm only, and
- shadow outer bindings of the same name (because they create a new binding in
 the arm’s environment).

### Exhaustiveness

In the Supported forms, enum exhaustiveness is split by [`match`](/silk/wiki/language/flow-match/) form:

- Expression form:
 - either there is exactly one arm per enum variant,
 - or a single final wildcard arm (`_ => ...`) covers the remaining unmatched
 variants,
 - explicit variant arms must not repeat the same variant,
 - and if `_` is used, it may appear at most once and must be the final arm.
- Statement form over ordinary enum values:
 - either there is exactly one arm per enum variant,
 - or a single final wildcard arm (`_ => ...`) covers the remaining unmatched
 variants,
 - explicit variant arms must not repeat the same variant,
 - and if `_` is used, it may appear at most once and must be the final arm.

If a match is not exhaustive:

- expression form currently reports `E2002`, and
- statement form without full coverage uses the checker’s missing-arm path when
 it reaches that analysis.

### Example: unit enum match

```silk
enum E {
  A,
  B,
}

fn main () -> int {
  let v: E = E::A;

  let x: int = match v {
    E::A => 10,
    E::B => 20,
  };

  if x != 10 {
    return 1;
  }
  return 0;
}
```

### Example: unit enum match with final wildcard

```silk
enum State {
  Ready,
  Busy,
  Closed,
}

fn score (s: State) -> int {
  return match s {
    Ready => 10,
    _ => 20,
  };
}
```

### Example: tuple enum match (payload binders)

```silk
enum E {
  Pair(int, int),
  Empty,
}

fn main () -> int {
  let v: E = E::Pair(1, 2);

  let x: int = match v {
    E::Pair(a, _) => a,
    E::Empty => 0,
  };

  if x != 1 {
    return 1;
  }
  return 0;
}
```

### Example: struct payload enum match

```silk
struct Job {
  id: int,
}

enum RecvJob {
  Msg(Job),
  Cancelled,
}

fn main () -> int {
  let j: Job = Job{ id: 5 };
  let evt: RecvJob = RecvJob::Msg(j);

  let rc: int = match evt {
    RecvJob::Msg(job) => job.id,
    RecvJob::Cancelled => 0,
  };

  if rc != 5 {
    return 1;
  }
  return 0;
}
```

## Representation

Enums are values. In the current IR-backed lowering, an enum value is lowered to
scalar slots as:

1. a `u64` **tag** (variant index in declaration order, starting at `0`), and
2. a **payload region** that includes a distinct slot range for each variant’s
 payload elements, in variant declaration order.

Conceptually:

```text
(u64 tag,
 payload slots for variant 0,
 payload slots for variant 1,
 ...)
```

Only the active variant’s payload region is meaningful for a given value; other
payload regions are unspecified.

This representation is an implementation detail and is expected to evolve (for
example, toward a tag + max-payload “union-style” layout) as the compiler and
ABI mature.

## Common Pitfalls

- **Forgetting parentheses**: `E::Data(7)` is valid, but `E::Data` is not a value
 in the Supported forms (error `E2002`).
- **Calling a unit variant**: `E::A` is a value; `E::A()` is rejected (`E2002`).
- **Wrong binder count**: `E::Pair(a)` does not match `Pair(int, int)` (`E2002`).
- **Non-exhaustive matches**: you must list every variant (error `E2002` in the
 Supported forms).
- **Assuming enum equality is defined**: use [`match`](/silk/wiki/language/flow-match/) to inspect the tag/payload;
 the backend does not define `==`/`!=` over enums yet.

## Related Documents

- [flow match](/silk/docs/language/flow-match/) (match expression rules)
- [structs impls layout](/silk/docs/language/structs-impls-layout/) (struct payloads)
- [types](/silk/docs/language/types/) (nominal types and type annotations)
- [packages imports exports](/silk/docs/language/packages-imports-exports/) (namespaces and imports)
- [typed errors](/silk/docs/language/typed-errors/) (typed errors, not enums)
