Labeled loop syntax in many languages
Many existing languages support labeled loops, such that you can say break foo;
to break
out of the loop labeled foo
, instead of just breaking from the smallest enclosing loop.
The C programming language just gained labeled loops, too, with the adoption of
N3355 “Named loops” (Alex Celeste, 2024).
Erich Keane and Aaron Ballman reacted to N3355’s adoption with N3377 “An Improved Syntax for N3355,” which I very much hope will be voted down. It’s an “interesting” proposal, in that the authors are compiler engineers who work on a polyglot compiler (Clang), and yet they don’t seem to consider consistency with other languages to be a positive for C at all.
Notably, Chris Lattner already implemented N3355’s syntax in Clang itself back in 2014, for Swift’s benefit. To be fair, mainline Clang’s corresponding codepath has over the past ten years diverged from Swift’s (e.g. it’s gone through a couple of renamings); but there’d be no fundamental difficulty in upstreaming Lattner’s patch.
Here, for the record, are all the languages I’ve found that support labeled loops today.
Ada — Cpp2 — D — Dart — Fortran — Go — Java — JavaScript – Kotlin — Odin — Perl — PL/I — PL/pgSQL — PowerShell — Rust — Swift
If you know any more languages, using any syntax — especially if you can find any language using syntax similar to N3377’s! — please email me and let me know. Also please email me if you can confirm or correct the PL/I code below.
Ada
(Godbolt.)
OUTER:
for x in 0..3 loop
for y in 0..3 loop
Put_Line(-(+"x=%d y=%d" & x & y));
if A(x, y) /= 0 then
exit OUTER;
end if;
end loop;
end loop OUTER;
Put_Line("Exited the outer loop");
Cpp2 (Cppfront)
(Godbolt. I don’t know how to make Godbolt run the code. Confirmed it works on my local machine.)
OUTER:
for 0..<4 do (x) {
for 0..<4 do (y) {
printf("x=%d y=%d\n", x, y);
if (A[x][y] != 0) {
break OUTER;
}
}
}
puts("Exited the outer loop");
D
(Godbolt.)
OUTER:
for (int x = 0; x < 4; ++x) {
for (int y = 0; y < 4; ++y) {
printf("x=%d y=%d\n", x, y);
if (A[x][y] != 0) {
break OUTER;
}
}
}
puts("Exited the outer loop");
Dart
(Godbolt.)
OUTER:
for (int x = 0; x < 4; ++x) {
for (int y = 0; y < 4; ++y) {
print("x=$x y=$y");
if (A[x][y] != 0) {
break OUTER;
}
}
}
print("Exited the outer loop");
Fortran
(Godbolt. Thanks to Ivan Tubert-Brohman for this one!)
OUTER: do x = 0, 3
do y = 0, 3
print '(''x='',i1,'' y='',i1)', x, y
if (A(y, x) /= 0) then
exit OUTER
end if
end do
end do OUTER
print '(A)', 'Exited the outer loop'
Go
(Godbolt.)
OUTER:
for x := 0; x < 4; x++ {
for y := 0; y < 4; y++ {
fmt.Println("x=%d y=%d", x, y)
if A[x][y] != 0 {
break OUTER
}
}
}
fmt.Println("Exited the outer loop")
Java
(Godbolt.)
OUTER:
for (int x = 0; x < 4; ++x) {
for (int y = 0; y < 4; ++y) {
System.out.printf("x=%d y=%d\n", x, y);
if (A[x][y] != 0) {
break OUTER;
}
}
}
System.out.printf("Exited the outer loop\n");
JavaScript
(Godbolt.)
OUTER:
for (let x = 0; x < 4; ++x) {
for (let y = 0; y < 4; ++y) {
console.log("x=" + x, "y=" + y);
if (A[x][y] != 0) {
break OUTER;
}
}
}
console.log("Exited the outer loop");
Kotlin
(Godbolt.)
OUTER@
for (x in 0..<4) {
for (y in 0..<4) {
println("x=%d y=%d".format(x, y))
if (A[x][y] != 0) {
break@OUTER
}
}
}
println("Exited the outer loop")
Odin
(Godbolt.)
OUTER:
for x in 0..<4 {
for y in 0..<4 {
fmt.printf("x=%d y=%d\n", x, y)
if A[x][y] != 0 {
break OUTER
}
}
}
fmt.println("Exited the outer loop")
Perl
OUTER:
for (my $x = 0; $x < 4; ++$x) {
for (my $y = 0; $y < 4; ++$y) {
print "x=$x y=$y\n";
if ($A[$x][$y] != 0) {
last OUTER;
}
}
}
print "Exited the outer loop\n";
PL/I
(I don’t know any way to test PL/I. Corrections welcome.)
main: proc options(main);
declare A int(4,4)
initial((9)0, 1, (5)0);
OUTER:
do x = 1 to 4;
do y = 1 to 4;
put list ('x=', x, ' y=', y)
if A(x,y) ^= 0 then do;
leave OUTER;
end;
end;
end;
put list('Exited the outer loop')
end;
PL/pgSQL (PostgreSQL)
(Copy the program below into Crunchydata’s Postgres Playground.)
create or replace function main() returns void as $$
declare
A integer array;
begin
A = '{{0,0,0,0},{0,0,0,0},{0,1,0,0},{0,0,0,0}}';
<<OUTER>>
for x in 0..3 loop
for y in 0..3 loop
raise info 'x=% y=%', x, y;
if A[x][y] then
exit OUTER;
end if;
end loop;
end loop;
raise info 'Exited the outer loop';
end;
$$ language plpgsql;
select main();
PowerShell
(TIO.)
:OUTER
for ($x = 0; $x -lt 4; ++$x) {
for ($y = 0; $y -lt 4; ++$y) {
"x=$x y=$y"
if ($A[$x][$y] -ne 0) {
break OUTER
}
}
}
"Exited the outer loop"
Rust
(Godbolt.)
'OUTER:
for x in 0..4 {
for y in 0..4 {
println!("x={} y={}", x, y);
if A[x][y] != 0 {
break 'OUTER;
}
}
}
println!("Exited the outer loop");
Swift
(Godbolt.)
OUTER:
for x in 0..<4 {
for y in 0..<4 {
print("x=\(x) y=\(y)");
if (A[x][y] != 0) {
break OUTER
}
}
}
print("Exited the outer loop")
Languages without labeled loops
C# has a lot of folks asking for labeled loops over at dotnet/csharplang#6634, but has never officially considered adding them as far as I know.
Python considered labeled loops in PEP 3136, but the idea was rejected as both unfocused (the PEP didn’t propose one syntax but rather brainstormed five possible syntaxes) and unnecessary.
Honestly, I don’t think C or C++ need labeled loops either — I think they’re unnecessary — but if we do get them, I hope it’s with the well-thought-out N3355 syntax shared by all the above modern languages, and not with N3377’s unprecedented and cumbersome syntax.