← Back to AILP Home

while

while is the condition-driven loop. The body runs as long as the boolean condition evaluates to true, and the condition is re-checked before every iteration.

func:failsafe = int32(tbb32:err) {
    exit 1;
};

func:main = int32() {
    int32:x = 3i32;
    int32:n = 0i32;
    while (x > 0i32) {
        n += 1i32;
        x -= 1i32;
    }
    exit n;   // 3
};

Re-evaluation

The condition is a live expression, not a snapshot. Anything it reads is read again on each pass, so the loop reacts to mutations made in the body:

while (x > 0i32) {
    x -= 1i32;   // observed by the next `x > 0i32` check
}

Mutating a parameter in the condition

A scalar function parameter may be mutated directly inside the loop that tests it. The parameter behaves like any local: the condition sees each update.

func:countdown = int32(int32:x) {
    while (x > 0i32) {
        x -= 1i32;
    }
    pass x;   // 0
};

Historical note (v0.38.8): earlier builds could spin forever here because a scalar parameter was bound to its raw SSA argument and the condition was generated once against the original value. Scalar int/float parameters are now promoted to stack storage in the prologue, so loop conditions observe in-body mutation. Regression coverage: bug908, bug909.

Empty and one-shot loops

while (false) { ... } never runs its body. A while whose condition is already false on entry contributes nothing — useful when pairing with an invariant, where the entry obligation must still hold for the zero-iteration case (see Invariants and contracts).

When to prefer a counted form

If the condition is really "count from a to b", reach for loop or till instead — the intent reads more clearly and the counter is managed for you.

Next: loop.