if / else if / else
The if statement runs a block when its condition is true. It may be followed
by zero or more else if clauses and an optional trailing else.
func:grade = int32(int32:score) {
if (score >= 90i32) {
println("A");
} else if (score >= 80i32) {
println("B");
} else if (score >= 70i32) {
println("C");
} else {
println("F");
};
pass(0i32);
};
Evaluation order
Conditions are tested top to bottom. The first one that is true runs its
block; all remaining clauses (including else) are skipped. If none is true
and there is an else, the else block runs; otherwise nothing runs.
This is the statement-level mirror of the is ternary's arm selection, and the
K semantics encodes exactly these cases:
if (true) B else _ => B
if (false) _ else B => B
if (true) B => B
if (false) _ => .K // nothing runs
Blocks and scope
Each arm is its own block with its own scope. A binding introduced in one arm is
not visible in another arm or after the if:
if (c) {
int32:t = compute(); // `t` lives only in this arm
use(t);
};
// `t` is not in scope here
A variable declared before the if and used in only one arm is still a
genuine use — you will not get a false [unused-variable] warning:
int32:limit = 10i32; // used only in the then-branch below — still "used"
if (c) {
pass(limit);
};
else if is a nested if
else if is not a separate construct; it is an if statement in the else
position of the previous one. That is why an assignment typo in an else if
condition produces the same ARIA-IF-002 diagnostic as one in the leading if
(see Diagnostics).
Dead code after a terminating branch
If a branch ends by leaving the function — for example via exit — any code
after the whole if that can only be reached when that branch did not fire is
still live. But code placed after an unconditional exit within a block is
dead, and the compiler flags it:
if (c) {
exit 3i32;
println("never runs"); // [dead-code]
};