Skip to content

RFC: syntax: Implement for/while-then & labeled, multi-level break#60367

Closed
Keno wants to merge 1 commit intomasterfrom
kf/breakbreakbreakcontinue
Closed

RFC: syntax: Implement for/while-then & labeled, multi-level break#60367
Keno wants to merge 1 commit intomasterfrom
kf/breakbreakbreakcontinue

Conversation

@Keno
Copy link
Member

@Keno Keno commented Dec 11, 2025

This implements multi-level and labeled break as contemplated in #5334. In addition this adds support for break-with-value (#22891) as well as for-then (aka for-else #1289). Also while-then of course. All three features are syntax gated to 1.14 syntax.

The syntax for multi-level break is as follows:

for i = 1:10
  for j = 1:10
    break break (i, j)
  end
end # evaluate to `(1,1)

The break value can be continue in which case the next innermost loop is continued:

julia> for i = 1:3
         for j = 1:3
           i > 1 && j == 2 && break continue
           @show (i,j)
         end
         @show i
       end
(i, j) = (1, 1)
(i, j) = (1, 2)
(i, j) = (1, 3)
i = 1
(i, j) = (2, 1)
(i, j) = (3, 1)

For more deeply nested loops, the loop can be annotated with a @label and the the break or continue can be targeted using @goto:

julia> @label outer for a = 1:2
         for b = 1:3
           for c = 1:4
             b > 1 && c == 2 && @goto outer break
           end
           @show b
         end
         @show a
      end
b = 1

Naturally continue is supported here as well.

The syntax and semantics for for-then are as proposed in the issue:

function has5(iter)
  return for x in iter
    x == 5 && break true
  then
    false
  end
end

Any break (including multi-level and labeled) skips the corresponding loop's then block, which ordinarily would run at loop completion.

I think the then keyword deserves some bikeshedding. I think the else keyword is seen as a bit of a mistake in languages that have it. Common lisp uses finally but I wanted to avoid it here since the semantics are substantially different from finally in a try-catch block (as mentioned in the original issue. Perl has a similar feature with a continue block (but it runs every time). Claude points out that django templates have an empty clause in for loops that runs if and only if the collection is empty (but doesn't have break in any case).

Another keyword options I thought about is normally. Kind of like finally, but for normal termination.

An additional motivation here is to make multi-level break available as syntax for the potential future addition of match (#60344).

Written by Claude. Some remaining cleanup and todos, but appears to basically be working.

@gbaraldi
Copy link
Member

I sent this to keno but I think it's a useful reference for anyone discussing this https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3568r0.html. This is the C++ proposal and it takes a look at what other languages do and what makes sense or not.

For my personal opinion I think that labels should be required for this feature, this maybe means we should introduce proper label syntax in Julia (which I'm not against). But forcing the use of a label means that code that was written like

for cond1
   for cond2
       ...
       break break
    end
 end

if over time the two loop statements get moved away from each other and then a new loop nesting is added, it continues to be clear and avoids the bug of jumping to the wrong place.

Also by always needing labels you can naturally extend to being able to break out of any kind of block, which may remove the need for a then.

This implements multi-level and labeled break as contemplated in #5334.
In addition this adds support for break-with-value (#22891) as well
as for-then (aka for-else #1289). Also while-then of course.
All three features are syntax gated to 1.14 syntax.

The syntax for multi-level break is as follows:
```
for i = 1:10
  for j = 1:10
    break break (i, j)
  end
end # Loop evaluate to `(1,1)
```

The break value can be `continue` in which case the next innermost
loop is continued:

```
julia> for i = 1:3
         for j = 1:3
           i > 1 && j == 2 && break continue
           @show (i,j)
         end
         @show i
       end
(i, j) = (1, 1)
(i, j) = (1, 2)
(i, j) = (1, 3)
i = 1
(i, j) = (2, 1)
(i, j) = (3, 1)
```

For more deeply nested loops, the loop can be annotated with a
`@label` and the the break or continue can be targeted using `@goto`:

```
julia> @Label outer for a = 1:2
         for b = 1:3
           for c = 1:4
             b > 1 && c == 2 && @goto outer break
           end
           @show b
         end
         @show a
      end
b = 1
```

Naturally `continue` is supported here as well.

The syntax and semantics for `for-then` are as proposed in the
issue:
```
function has5(iter)
  return for x in iter
    x == 5 && break true
  then
    false
  end
end
```

Any `break` (including multi-level and labeled) skips the corresponding
loop's `then` block, which ordinarily would run at loop completion.
@Keno Keno force-pushed the kf/breakbreakbreakcontinue branch from 88eedc4 to 0df79d3 Compare December 11, 2025 16:38
@Keno
Copy link
Member Author

Keno commented Dec 11, 2025

Yeah, as I said on slack, I think that's a reasonable way to go as long as we have proper syntax for labeled blocks. Rust's syntax of 'label: is currently a syntax error, so we could just steal it. I'll have claude code up that version as well (but probably not until next week).

Keno added a commit that referenced this pull request Dec 25, 2025
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults
to `_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm not interest in dicussion of
the syntax.
@Keno
Copy link
Member Author

Keno commented Dec 25, 2025

Superseded by #60481

@Keno Keno closed this Dec 25, 2025
@giordano giordano deleted the kf/breakbreakbreakcontinue branch December 26, 2025 00:06
Keno added a commit that referenced this pull request Dec 26, 2025
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults
to `_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm not interest in dicussion of
the syntax.
Keno added a commit that referenced this pull request Dec 26, 2025
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults
to `_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm not interest in dicussion of
the syntax.
Keno added a commit that referenced this pull request Jan 1, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults
to `_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm not interest in dicussion of
the syntax.
Keno added a commit that referenced this pull request Jan 3, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults
to `_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm not interest in dicussion of
the syntax.
Keno added a commit that referenced this pull request Jan 6, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm just interested in dicussion of
the syntax.
Keno added a commit that referenced this pull request Jan 6, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm just interested in dicussion of
the syntax.
Keno added a commit that referenced this pull request Jan 23, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label name for i = 1:10
  for j = 1:10
    break name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label outer while true
  for i = 1:10
    a[i] && continue outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label error begin
    cond1 && break error
    cond2 && break error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label result begin
  for x in arr
    pred(x) && break result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude.
Keno added a commit that referenced this pull request Jan 23, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label name for i = 1:10
  for j = 1:10
    break name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label outer while true
  for i = 1:10
    a[i] && continue outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label error begin
    cond1 && break error
    cond2 && break error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label result begin
  for x in arr
    pred(x) && break result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude.
Keno added a commit that referenced this pull request Jan 23, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label name for i = 1:10
  for j = 1:10
    break name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label outer while true
  for i = 1:10
    a[i] && continue outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label error begin
    cond1 && break error
    cond2 && break error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label result begin
  for x in arr
    pred(x) && break result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude.
Keno added a commit that referenced this pull request Jan 23, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the
   loop and the break.

Instead, the consensus seemed to be that labeled break should be
the only facility available. Thus, this implements a proper labeled
break facility that looks as follows:

```
@Label name for i = 1:10
  for j = 1:10
    break name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note
that parser changes are still required, since the `break` syntax is
currently an error. However, compat.jl could provide `@goto break name
val`, which parses fine.

`continue` is extended with label support as well:
```
@Label outer while true
  for i = 1:10
    a[i] && continue outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use
case is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label error begin
    cond1 && break error
    cond2 && break error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of
pattern much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label result begin
  for x in arr
    pred(x) && break result x
  end
  default
end
```

For anonymous blocks, use `@label _ begin ... end` with `break _`:

```
result = @Label _ begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and
to give an idea of what the syntax looks like in practice, but didn't
go through particularly comprehensively. These changes should
be considered extended usage examples.

Largely written by Claude.
Keno added a commit that referenced this pull request Jan 24, 2026
This is a redo of #60367 taking into account various feedback on that
PR. In particular, it seemed like people strongly disliked the `break
break` syntax for two primary reasons:

1. It scales poorly to multiple loops `break break break break`
2. It introduced footguns if extra loops are introduced between the loop
and the break.

Instead, the consensus seemed to be that labeled break should be the
only facility available. Thus, this implements a proper labeled break
facility that looks as follows:

```
@Label :name for i = 1:10
  for j = 1:10
    break :name (i, j)
  end
end # evaluate to `(1,1)
```

The idea is to re-use the `@label` macro for now, but possibly promote
this to syntax in a future version if it gains widespread use. Note that
parser changes are still required, since the `break` syntax is currently
an error. However, compat.jl could provide `@goto break name val`, which
parses fine.

`continue` is extended with label support as well:
```
@Label :outer while true
  for i = 1:10
    a[i] && continue :outer
  end
  [...]
end
```

However, the feature is not restricted to loops. A particular use case
is to replace cleanup blocks written like

```
cond1 && @goto error
cond2 && @goto error

return true

@Label error
error("foo")
```

by a labeled begin/end block:
```
@Label :error begin
    cond1 && break :error
    cond2 && break :error
    return true
end
error("foo")
```

This doesn't save much typing work, but it makes this kind of pattern
much more structured, e.g. for code-folding in IDEs.

A similar pattern replaces the `for-then` construction originally
proposed:

```
result = @Label :result begin
  for x in arr
    pred(x) && break :result x
  end
  default
end
```

For convenience, the label may be ommitted, in which case it defaults to
`_`, i.e. the above can be written as:

```
result = @Label begin
  for x in arr
    pred(x) && break _ x
  end
  default
end
```

I've taken the liberty of converting some base code for testing and to
give an idea of what the syntax looks like in practice, but didn't go
through particularly comprehensively. These changes should be considered
extended usage examples.

Largely written by Claude, and I haven't looked at the implementation
particularly carefully yet - for now I'm just interested in discussion
of the syntax.

---------

Co-authored-by: Keno Fischer <Keno@users.noreply.github.com>
@nsajko nsajko added parser Language parsing and surface syntax feature Indicates new feature / enhancement requests labels Jan 24, 2026
@Keno Keno mentioned this pull request Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Indicates new feature / enhancement requests parser Language parsing and surface syntax

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants