

# [`match`](/silk/wiki/language/flow-match/) Expression (and Statement)

The [`match`](/silk/wiki/language/flow-match/) expression provides structured pattern matching.

Key ideas:

- A [`match`](/silk/wiki/language/flow-match/) selects one of several branches based on a scrutinee expression.
- The full language design includes richer patterns and arm guards, but the
 current shipped subset documented here does not implement `if` guards in any
 [`match`](/silk/wiki/language/flow-match/) form yet.
- [`match`](/silk/wiki/language/flow-match/) is an expression; all arms must be compatible in type.

The compiler must:

- Enforce exhaustiveness rules (where specified).
- Type check each arm and compute a consistent result type.

## Surface Syntax

The full language design includes rich pattern matching, guards, and matching
over many scrutinee types. The current compiler implementation supports only a
narrow, explicitly documented subset so we can validate end-to-end lowering and
code generation.

In the initial subset, [`match`](/silk/wiki/language/flow-match/) is accepted as an *expression* of the form:

```silk
match <scrutinee> {
  <pattern> => <expr>,
  <pattern> => <expr>,
}
```

Notes:

- Arms are separated by commas; a trailing comma is permitted.
- In the initial subset, arm bodies are expressions (not blocks).
- In the current compiler, expression-form [`match`](/silk/wiki/language/flow-match/) is implemented for:
 - optionals,
 - primitive integers,
 - primitive booleans,
 - primitive strings,
 - enums,
 - type unions,
 - and recoverable `Result`-style values.
- Guard clauses of the form `pattern if cond => ...` are currently unsupported
 across all of those subsets.

### Optional Matching (`T?`)

For optionals, the Supported forms is:

- The scrutinee expression must have optional type `T?` (`Option(T)`), where `T`
 is a payload type supported by the backend.
- Patterns are restricted to:
 - `None`
 - `Some(<name>)`
 - `Some(_)`
- No guards (`if ...`) are implemented yet.
- Matches must be exhaustive for the optional scrutinee: there must be exactly
 one `None` arm and exactly one `Some(...)` arm (order is not significant).

Example:

```silk
fn main () -> int {
  let x: int? = Some(7);
  let y: int = match x {
    None => 5,
    Some(v) => v,
  };
  return y;
}
```

### Integer Matching (Primitive Integers)

The compiler also supports a small [`match`](/silk/wiki/language/flow-match/) subset for integer-like primitive
scrutinees.

Notes: 

- The scrutinee expression must have a primitive integer type in the current
 backend subset (`int`, `i64`, `u64`, and the fixed-width integer primitives).
- Patterns are restricted to:
 - integer literals (`0`, `1`, `123`, `0xFF`, ...), and
 - a wildcard `_` arm.
- The match must be exhaustive:
 - when the scrutinee is not a known integer literal, there must be exactly one
 wildcard `_` arm, and that arm must be final,
 - when the scrutinee is a known integer literal, the wildcard may be omitted
 only if one integer-literal arm covers that exact value, and
 - literal arms must not repeat the same value.
- No guards (`if ...`) are implemented yet.

Example:

```silk
fn main () -> int {
  let x: int = 0;
  let y: int = match (x) {
    0 => 1,
    _ => 2,
  };
  return y;
}
```

Literal scrutinees may be exhaustively matched by the literal arms alone:

```silk
fn main () -> int {
  let y: int = match 2 {
    1 => 10,
    2 => 20,
  };
  return y;
}
```

### Boolean Matching (Primitive `bool`)

The compiler also supports [`match`](/silk/wiki/language/flow-match/) over primitive `bool` scrutinees.

Supported forms:

- The scrutinee expression must have type `bool`.
- Patterns are restricted to:
 - boolean literals (`false` and `true`), and
 - a wildcard `_` arm.
- The match must be exhaustive:
 - when the scrutinee is not a known boolean literal, it must cover both
 `false` and `true`, or have exactly one final wildcard `_` arm,
 - when the scrutinee is a known boolean literal, the wildcard may be omitted
 only if one boolean-literal arm covers that exact value, and
 - literal arms must not repeat the same boolean value.
- No guards (`if ...`) are implemented yet.

Example:

```silk
fn main () -> int {
  let b: bool = false;
  let y: int = match b {
    false => 1,
    true => 2,
  };
  return y;
}
```

### String Matching (Primitive `string`)

The compiler also supports [`match`](/silk/wiki/language/flow-match/) over primitive `string` scrutinees.

Supported forms:

- The scrutinee expression must have type `string`.
- Patterns are restricted to:
 - string literals (`"..."` and raw backtick strings), and
 - a wildcard `_` arm.
- Literal comparison uses the decoded byte sequence of each string literal.
- The match must be exhaustive:
 - when the scrutinee is not a known string literal, it must have exactly one
 final wildcard `_` arm,
 - when the scrutinee is a known string literal, the wildcard may be omitted
 only if one string-literal arm covers that exact decoded byte sequence, and
 - literal arms must not repeat the same decoded byte sequence.
- No guards (`if ...`) are implemented yet.

Example:

```silk
fn main () -> int {
  let s: string = "foo";
  let y: int = match s {
    "foo" => 1,
    _ => 2,
  };
  return y;
}
```

Literal scrutinees may be exhaustively matched by the literal arms alone:

```silk
fn main () -> int {
  let y: int = match "foo" {
    "foo" => 1,
    "bar" => 2,
  };
  return y;
}
```

### Enum Matching (`enum`)

The language design supports matching over user-defined `enum` types
([enums](/silk/docs/language/enums/)).

Notes: 

- The scrutinee expression must have an enum type `E` (including an
 instantiated generic enum in module-set builds).
- Patterns are restricted to enum variants:
 - unit variants: `E::Cancelled` or `Cancelled`
 - tuple variants: `E::Msg(x)` / `Msg(x)` / `E::Pair(a, b)` / `Pair(a, b)` (binders may be identifiers or `_`)
- For instantiated generic enums, the qualifier `E` in patterns may be a type
 alias for the instantiation (for example `type R = Result(int, string);` then
 `R::Ok(v)` / `R::Err(e)`), or patterns may omit the qualifier and use the
 variant name directly.
- No guards (`if ...`) are implemented yet.
- In expression form, enum matches must still be exhaustive:
 - either there is exactly one explicit arm for each enum variant,
 - or a final wildcard `_` arm covers every remaining unmatched variant,
 - and if `_` is used, it may appear at most once and must be the final arm.

### Type Union and Concrete Typed-Binder Matching

The language supports matching over **type unions** ([type unions](/silk/docs/language/type-unions/)).
The same typed-binder arm syntax is also accepted for concrete struct values in
the backend when the checker can select exactly one applicable
arm statically.

Notes: 

- For a union scrutinee, the scrutinee expression must have a union type
 `T1 | ... | Tn`.
- For a concrete struct scrutinee, the scrutinee expression must have a known
 concrete struct type, and exactly one typed-binder arm must accept that type.
- Patterns are restricted to typed binders:
 - `name: Ti` (binds the payload as `Ti`), or
 - `_: Ti` (matches and ignores the payload),
 where `Ti` is one of the union member types for union scrutinees, or an
 accepting nominal type for concrete scrutinees.
- For concrete struct scrutinees, an arm type accepts the scrutinee when it is:
 - the exact concrete type or an alias of it,
 - a valid base type in the struct `extends` chain, or
 - an interface implemented by the concrete type.
- No guards (`if ...`) are implemented yet.
- Union matches must be exhaustive: there must be exactly one arm per union
 member type (order is not significant).
- Concrete typed-binder matches are statically selected: there must be exactly
 one accepting arm. Zero accepting arms are rejected as a missing arm, and more
 than one accepting arm is rejected as an ambiguous typed-binder match.

Example:

```silk
interface Object {}
struct A { x: int }
struct B { x: int }
impl A as Object {}

fn main () -> int {
  let a = A { x: 1 };
  let y = match a {
    v: A => v.x,
    _: B => 0,
  };
  let z = match a {
    _: Object => 2,
  };
  return y + z;
}
```

## Semantics

- The scrutinee expression is evaluated exactly once.
- The selected arm is chosen based on the scrutinee value; non-selected arms
 are not evaluated.
- For `Some(v) => ...`, the binder `v` is in scope only within that arm and has
 type `T` (the inner payload type of the scrutinee `T?`).
- For typed-binder arms `v: T => ...`, the binder `v` is in scope only within
 that arm and has the annotated type `T`; `_: T` checks the same type contract
 without introducing a binder.
- The result type of a [`match`](/silk/wiki/language/flow-match/) expression is the common type of its arms; all
 arms must type-check to the same result type in the initial subset.
- Pattern binders for ownership-tracked payloads are owners, not borrowed
 aliases. When an arm consumes such a binder as its result, including by
 wrapping it in another enum or optional constructor, ownership transfers to
 the result and the selected payload in the scrutinee becomes uninitialized.
 Cleanup therefore destroys the returned owner exactly once and does not
 destroy the transferred source payload. Arms that do not consume their
 binder leave the selected payload owned by the scrutinee.
- A tuple variant may contain multiple ownership-tracked payloads. If an arm
 transfers only some payload binders, the selected arm destroys every
 unconsumed payload before invalidating the source variant; transferred
 payloads remain live only in the arm result. An `_` payload binder is
 unconsumed and follows the same destruction rule.
- This path-sensitive transfer rule applies uniformly to user enums,
 `Some(T)`, and the `Ok(T)` / `Err(E)` payloads of recoverable results. It is
 preserved across nested compositions such as an enum payload returned as an
 optional or an optional payload returned as another enum.

## [`match`](/silk/wiki/language/flow-match/) Statement (Block Arms)

Silk also supports a *statement* form of [`match`](/silk/wiki/language/flow-match/) whose arms are blocks. This is
the ergonomic counterpart to the expression form when an arm must perform
multiple statements (printing, early returns, mutation, etc).

Surface form:

```silk
match (<scrutinee>) {
  <pattern> => { ... },
  <pattern> => { ... },
}
```

The statement form may also use a single-expression arm body without braces:

```silk
match (x) {
  _ => do_work(),
}
```

An optional trailing semicolon is permitted after the closing brace:

```silk
match (x) { _ => { } };
```

In Silk currently, the statement form is supported for:

- ordinary value matching (no typed-error contract), and
- typed error handling ([typed errors](/silk/docs/language/typed-errors/)).

### Ordinary value matching

When the scrutinee expression is an ordinary value (it does *not* have a typed
error contract), the statement form supports the same scrutinee + pattern
subsets as the [`match`](/silk/wiki/language/flow-match/) expression form in this document:

- optionals (`T?`): `None` / `Some(name)` / `Some(_)`
- primitive integers: integer literals and `_`
- primitive booleans: `false` / `true` and `_`
- primitive strings: string literals and `_`
- enums (`enum`): enum variants (see note below)
- type unions (`T1 | ... | Tn`): typed binders `name: Ti` / `_: Ti`
- recoverable results: `Ok(name)` / `Ok(_)` and `Err(name)` / `Err(_)`

Exhaustiveness rules:

- Expression [`match`](/silk/wiki/language/flow-match/) remains exhaustive.
- Statement [`match`](/silk/wiki/language/flow-match/) is also exhaustive by default.
- For `Option(T)` and recoverable `Result`-like values only, the statement form
 may omit one side of the split:
 - `match (opt) { Some(v) => { ... } }`
 - `match (opt) { None => { ... } }`
 - `match (res) { Ok(v) => { ... } }`
 - `match (res) { Err(e) => { ... } }`
- In that one-arm statement form, the unhandled case is an implicit no-op.
- This partial form does not apply to expression [`match`](/silk/wiki/language/flow-match/), integer matches,
 general enums, type unions, or typed-error matches.

Notes:

- The preferred single-branch control-flow forms remain `if let`, `let ... else`,
 and `while let` when they fit naturally.
- For ordinary enum matches, both expression and statement form now support one
 final wildcard `_` arm end to end.
- That wildcard arm:
 - may appear at most once,
 - must be the final arm,
 - and must cover at least one still-unmatched variant.

Enum variant pattern note (statement form):

- In the statement form, a bare identifier pattern `name` is reserved for a
 catch-all binder arm (used by typed error matches), so enum variant patterns
 must be written in qualified form: `E::Variant(...)` (including
 `::pkg::E::Variant(...)`).
- `match (stream) { Error => { ... } }` therefore treats `Error` as a binder
 arm, while `match (stream) { IOStream::Error => { ... } }` matches the unit
 enum variant.
- Ordinary enum statement matches now also allow one final `_` catch-all arm,
 with the same final-arm and non-redundancy rules as expression-form enum
 matches.

### Typed error matching (Terminal Arm Rule)

The language design also includes a statement form of [`match`](/silk/wiki/language/flow-match/) used for
*typed errors* ([typed errors](/silk/docs/language/typed-errors/)).

Surface form:

```silk
match (expr) {
  pattern => { ... },
  err: SomeError => { std::abort(); }
}
```

Key semantic rule (Terminal Arm Rule):

- If `expr` is an error-producing expression (its signature includes `T | ErrorType...`),
 then any arm that matches an `error` type must end in a terminal statement.

Implementation

- The compiler currently implements [`match`](/silk/wiki/language/flow-match/) as an expression for the documented
 Supported forms:
 - optionals (`T?`),
 - primitive integers,
 - type unions,
 - recoverable `Result`-style values,
 - and exhaustive enum matches.
- No match arm guards (`pattern if cond => ...`) are implemented yet in either
 expression or statement form.
- The statement form is implemented for:
 - ordinary values in the supported subset (block arms), and
 - typed errors as part of the typed errors feature work ([typed errors](/silk/docs/language/typed-errors/)).

Note: the compiler also allows the [`match`](/silk/wiki/language/flow-match/) statement form to destructure
recoverable `Result`-style values. This form does not trigger the Terminal Arm
Rule because it is not a `T | ...` typed-error expression.

### Result Matching (`Ok(...)` / `Err(...)`)

The [`match`](/silk/wiki/language/flow-match/) expression also supports a small subset for
recoverable “success or error” values. In the initial subset, this includes:

- [`std::result::Result(T, E)`](/silk/docs/std/result/) (an `enum` with `Ok(T)` and `Err(E)` variants), and
- “Result-like” structs of the form `{ value: T?, err: E? }`.

For the struct form, the runtime invariant is: exactly one of `value` and `err`
is `Some(...)`. If this invariant is broken at runtime, execution traps.

Patterns:

- `Ok(name)` / `Ok(_)`
- `Err(name)` / `Err(_)`

Rules (Supported forms):

- Enum form:
 - The scrutinee expression must have an enum type with variants `Ok` and `Err`.
 - `Ok(...)` / `Err(...)` patterns are shorthand for `R::Ok(...)` / `R::Err(...)` where `R`
 is the scrutinee enum type, and may appear alongside other enum variant patterns.
 - In expression form, exhaustiveness follows the enum rules: there must be
 exactly one arm per enum variant.
- Struct form:
 - The scrutinee expression must have a nominal struct type that contains
 `value: T?` and `err: E?`.
- Matches must be exhaustive:
 - for enum scrutinees in expression form, follow the enum rules (one arm per
 variant, or a single final wildcard arm covers the remaining unmatched
 variant),
 - for struct scrutinees, there must be exactly one `Ok(...)` arm and exactly
 one `Err(...)` arm.
- In `Ok(v) => ...`, the binder `v` has type `T`.
- In `Err(e) => ...`, the binder `e` has type `E`.

Example:

```silk
import std::result;
import std::strings::String;

fn main () -> int {
  let s: String = match String.from_string("hello") {
    Ok(v) => v,
    Err(_) => String.empty(),
  };
  return s.len as int;
}
```

One-arm statement examples:

```silk
match (parse_port(input)) {
  Ok(port) => {
    use_port(port);
  },
}
```

```silk
match (std::env::get("HOME")) {
  None => {
    std::io::println("HOME is not set");
  },
}
```
