Wado

WEP: let ... else Statements

Context

A refutable pattern in a plain let is a compile error — let demands an irrefutable pattern. The common shape "bind this or bail out early" therefore needs if let, which pushes the happy path one level of indentation to the right and leaves the bindings scoped to the nested block:

if let Ok(port) = i32::from_str(&s) {
    // everything that uses `port` lives here, indented
    ...
} else {
    return -1;
}

Rust solves this with let ... else (RFC 3137). Wado adopts the same construct.

Decision

Syntax

let PATTERN = EXPR else { DIVERGING_BLOCK };
fn parse_port(s: String) -> i32 {
    let Ok(port) = i32::from_str(&s) else {
        return -1;
    };
    return port;              // `port` in scope
}

Because break/continue diverge, a let ... else inside a loop can skip or stop iteration on a failed bind:

for let it of items {
    let Some(n) = it else { continue; };
    sum += n;
}

Implementation

LetStmt gains an else_block: Option<Block>. The parser fills it when an else follows the initializer (statement position only — a C-style for-loop initializer has no "rest of block" to guard, so else is not accepted there).

let ... else desugars, at reify time, into a two-arm Match — the same lowering if let uses (reify_let_chain_stmts), with one twist: the then-arm is the rest of the enclosing block, so the pattern's bindings are in scope for it, and the wildcard arm is the diverging else block:

{ ...; let PAT = EXPR else { ELSE }; REST }
⇒
{ ...; match EXPR { PAT => { REST }, _ => { ELSE } } }

The else block is resolved/reified before the pattern bindings enter scope, so it cannot reference them; the scrutinee, else block, pattern, and continuation are walked in the same order in the records-only resolve pass and the TIR-building reify pass, keeping the monotonic local-index allocation identical between the two.

Divergence is checked with the existing AST control-flow analysis (control_flow::block_always_exits), extended to treat a break/continue statement as exiting (it already counted them as Never in block_result_type).

Consequences