Patterns
Patterns are used in match expressions and match statements to destructure values and bind their components to names.
Pat :=
"#" Ident
| "@" Ident
| "@" Ident "(" ArgList ")"
| WordLit
| BitLit
Enum Patterns
Enum patterns match a value against a specific enum variant.
Pat :=
"#" Ident
The # syntax selects a named variant of an enum type.
wire decoded : Word[8]
decoded := match op {
case #OP => 0x33
case #OP_IMM => 0x13
case #LOAD => 0x03
case #STORE => 0x23
}
The pattern #OP matches only the OP variant of the enum type. The scrutinee and the patterns must belong to the same enum type.
Union Patterns
Union patterns match a value against a specific variant of a union type, optionally binding the variant’s associated data to names.
Pat :=
"@" Ident
| "@" Ident "(" ArgList ")"
The @ syntax selects a variant of a union type.
For a variant with no associated data:
wire result : Bit
result := match maybe {
case @Nothing() => false
case @Just(_) => true
}
For a variant with associated data, the pattern binds each field to a name:
wire decoded : MyEnum
decoded := match u {
case @Foo() => #Baz
case @Bar(x) => x
}
The bound names behave like variables that are in scope only within the matched arm’s expression or statement block. They are not registers or wires — they are read-only bindings to the components of the matched union value.
Valid Patterns
The builtin Valid[T] type supports two patterns for deconstructing optional values:
Pat := "@" "Valid" "(" ArgList ")" | "@" "Invalid" "(" ")"
@Valid(x) matches a valid value and binds the inner value to x.
@Invalid() matches an invalid (absent) value.
wire got_it : Bit
got_it := match v {
case @Valid(x) => x
case @Invalid() => false
}
These patterns are checked against the Valid[T] type by the compiler. The scrutinee must be of type Valid[T] for either pattern to apply.
Literal Patterns
Literal patterns match a value against a specific constant.
Pat :=
WordLit
| BitLit
wire parity : Bit
parity := match 2w2 {
case 0 => false
case 1 => true
case 2 => false
case 3 => true
}
Literal patterns support both Bit literals (true, false) and Word literals (0, 42w8, 0xff).
Exhaustiveness
Match expressions and statements must be exhaustive. This means that every possible value of the scrutinee type must be covered by at least one pattern.
For enum types, exhaustiveness is checked by ensuring every variant appears in at least one arm.
enum type State width 2 {
Idle = 0
Busy = 1
Done = 2
}
// Error: missing variant 'Done'
wire next : State
next := match state {
case #Idle => #Busy
case #Busy => #Done
}
For union types, exhaustiveness is checked by ensuring every variant appears in at least one arm.
For literal patterns, exhaustiveness requires that every value of the word’s bit width is covered, which is often impractical. In such cases, an else arm should be used as a catch-all.
wire parity : Bit
parity := match 2w2 {
case 0 => false
case 1 => true
case 2 => false
case 3 => true
}
// All 4 values of Word[2] are covered, so this is exhaustive
The else Arm
Both match expressions and match statements support an else arm that matches any value not covered by the preceding case arms.
wire decoded : Word[8]
decoded := match op {
case #OP => 1
case #OP_IMM => 2
else => 0
}
The else arm must appear last, after all case arms. Only one else arm is allowed per match.
Patterns in Match Statements
Patterns are used identically in both match expressions and match statements. In a match statement, the arm body is a block of module body statements instead of a single expression.
match union_val {
case @Foo() => {
out := 0
flag := true
}
case @Bar(x) => {
out := x
flag := false
}
}
Inside the arm body, bindings from the pattern (like x in the @Bar(x) case) are available as read-only values.