LEVIATHAN v962456e · 962456eee1

Statements

Labeled loops

Naming a loop so that break and continue can leave or restart an outer loop from any depth.

since 0.1.0-alpha.1linuxwindowswasm

Description

A loop can carry a label: an identifier followed by a colon, written immediately before while, do, or either form of for. break label; and continue label; then name a loop that encloses the statement, in the same function body, and act on that loop however deeply the statement is nested. Unlabeled break; and continue; are unchanged: they act on the nearest enclosing loop, labeled or not.

Leaving and restarting an outer loop

outer: for (int i = 0; i < 3; i = i + 1) {
    for (int j = 0; j < 3; j = j + 1) {
        if (j == 2) continue outer;
        if (i == 2) break outer;
        console.write("${i}${j};");
    }
}
console.writeln("");

int k = 0;
a: while (k < 2) {
    k = k + 1;
    b: do {
        console.write("k${k};");
        break a;
    } while (true);
}
console.writeln("");
00;01;10;11;
k1;

Rules

  • A loop has at most one label. a: b: while (...) is a parse error.
  • Labels live in their own namespace: a loop labeled L and a variable named L do not collide.
  • A label may not reuse the label of an enclosing loop; that is a compile error (label 'L' is already used by an enclosing loop). Two sibling loops, neither enclosing the other, may use the same label.
  • A lambda body is a label boundary, as it is for plain break and continue. A labeled break or continue inside a lambda cannot name a label of the enclosing function: it fails with no enclosing loop is labeled 'L', the same error an unknown label produces.
  • match is not a label boundary. A labeled break or continue in a match arm inside a labeled loop applies to that loop.
  • A labeled break or continue that crosses using declarations closes exactly the resources declared inside the target loop, in reverse order of declaration, and none declared outside it. This is the same cleanup guarantee as an ordinary using, applied to a multi-level exit.

Examples

Labeled exits close the resources of the loops they leave

class Res : IDisposable {
    string name;
    new Res(string n) {
        name = n;
        console.writeln("open ${n}");
    }
    void close() {
        console.writeln("close ${name}");
    }
}

outer: for (int i = 0; i < 3; i = i + 1) {
    using Res row = Res("row${i}");
    int j = 0;
    inner: while (j < 3) {
        using Res cell = Res("cell${i}${j}");
        j = j + 1;
        if (i == 0 && j == 1) continue inner;
        if (i == 0) continue outer;
        if (i == 1) break outer;
    }
}
console.writeln("done");
open row0
open cell00
close cell00
open cell01
close cell01
close row0
open row1
open cell10
close cell10
close row1
done
outer: while (true) {
    outer: while (true) { break outer; }
}
// error: label 'outer' is already used by an enclosing loop

x: while (true) {
    var f = () => { break x; };
    f();
}
// error: no enclosing loop is labeled 'x'

a: b: while (true) { }
// error: expected ';'
not run — shows a compile error

Notes

  • continue inner closes the cell resource of the current pass, because cell is declared inside the inner loop that the statement restarts. continue outer closes both cell and row, because row is declared inside the outer loop it restarts.

See also

  • IDisposable — The interface of an object that must be cleaned up when its work is finished.