Statements
Statements are the runtime steps that fill function bodies and blocks. Every statement ends with ;, except the ones that end with a block {...}.
Conditionals: if / or
An if head opens a branch chain. Each or (cond) { ... } adds another conditional branch, and a trailing or { ... } with no condition is the catch-all. Every body is a block; there is no brace-less single-statement form.
if (cond) {
...
}
or (cond) {
...
}
or {
... # final else (no condition)
}
Lexical payload guards
An if (sel p.c) condition opens a compile-time payload guard for the case p.c inside its block. Reading p.c is only permitted within such a dominating guard.
Guards also propagate across logical &&: the right side of sel p.c && expr is guarded by the check on the left. In addition, when an if branch exits the enclosing block using ret, brk, or cnt, the compiler knows the exited case cannot reach subsequent statements.
An or branch is not guarded because the tested condition failed. Logical || and ! do not establish guards. Keep tests to one condition per check when unwrapping payloads.
Loops: for
mach has a single condition-driven loop. The body repeats while the condition holds. There is no for-each form.
var i: i64 = 0;
for (i < 10) {
i = i + 1;
}
Returning: ret
ret expr; returns a value; bare ret; returns from a void function.
ret expr; # return a value
ret; # return from a void function
Loop control: brk / cnt
brk exits the enclosing for; cnt skips to the next iteration.
for (i < 10) {
if (i == 3) { cnt; }
if (i == 8) { brk; }
i = i + 1;
}
Both are operand-less, so they are keywords only in their bare brk; / cnt; form. The same word followed by anything else is an ordinary identifier: a variable named cnt reads and assigns normally (cnt = x;), even inside a loop that also uses bare cnt; for control flow.
Deferred cleanup: fin
fin schedules a statement or block to run when the enclosing scope exits, in reverse order of declaration. It is for cleanup that should happen regardless of how the scope is left.
{
fin counter = counter - 1;
fin { counter = counter * 2; }
# ... code ...
}
# at scope exit: fin block runs first, then fin counter = counter - 1
fin takes either a single statement (fin stmt;) or a block (fin { ... }). There is no bare-expression form.
Blocks
{ ... } introduces a new lexical scope. Statements inside run in order, and a block can stand alone to bound the lifetime of locals.
{
val tmp: i64 = compute();
use_tmp(tmp);
}
See also
- Expressions - what goes on the right side of
=and inside conditions - Values and variables -
varandvaldeclarations - Records, unions, and tags - tag declarations and payload safety
- Control flow - the comptime counterparts
$ifand$each