# Goto Statement

A `goto` statement was added in Lua 5.2.0-beta-rc1 [6] [1] and refined in 5.2.0-beta-rc2 [7]. This is a restrictive form of `goto` in that

• A label is visible in the entire block where it is defined (including nested blocks, but not nested functions).
• A goto may jump to any visible label as long as it does not enter into the scope of a local variable. [1]

This rest of this page explores some usages of this new construct.

### Nested break

```-- 5.2.0-beta-rc2
for z=1,10 do
for y=1,10 do
for x=1,10 do
if x^2 + y^2 == z^2 then
print('found a Pythagorean triple:', x, y, z)
goto done
end
end end end
::done::
```

### Continue or nested continue

```-- 5.2.0-beta-rc2
for z=1,10 do
for y=1,10 do
for x=1,10 do
if x^2 + y^2 == z^2 then
print('found a Pythagorean triple:', x, y, z)
print('now trying next z...')
goto zcontinue
end
end end ::zcontinue:: end
```

### Perl style redo [2]

```-- Lua 5.2.0-beta-rc2
for x=1,5 do ::redo::
print(x .. ' + 1 = ?')
if y ~= x + 1 then goto redo end
end
```

### Pythonic for-else [3]

```-- Lua 5.2.0-beta-rc2
for _, x in ipairs(t) do
if x % 2 == 0 then
print 'list has even number'
goto has
end
end
print 'list lacks even number'
::has::
```

```-- Lua 5.1 equivalent
local has
for _, x in ipairs(t) do
if x % 2 == 0 then
has = true
break
end
end
if has then
print 'list has even number'
else
print 'list lacks even number'
end
```

### State machine or Markov chain

```-- 5.2.0-beta-rc1
::a::
print 'A'
if math.random() < 0.3 then goto c end
::b::
print 'B'
if math.random() < 0.5 then goto a end
::c::
print 'C'
if math.random() < 0.1 then goto a else goto b end
```

### Simulated tail call

Lua already has ProperTailRecursion, but in the hypothetical case it did not, we could simulate tail calls with `goto` (as some of the C source code of Lua does):

```-- 5.2.0-beta-rc2 - factorial with tail recursion simulated with goto's
-- (warning: there's no need to do this)
function fact_(n, ans)
::call::
if n == 0 then
return ans
else
n, ans = n - 1, ans * n
goto call
end
end
print(fact_(5, 1)) --> 120
```

### Error handling and cleanup

```-- 5.2.0-beta-rc2
function f()
if not g() then goto fail end
if not h() then goto cleanup_g end
if not i() then goto cleanup_h end
do return true end    -- need do/end?

::cleanup_h::
undo_h()
::cleanup_g::
undo_g()
::fail::
return false
```
(from [9])

### Switch statement

Not possible without computed `goto` [4]. See also SwitchStatement.

### Conventions for Labels

It may help readability for label names to indicate the direction (up or down) that they jump. [10] In the example below, it may be conventionally understood that the names `continue` and `skip` will jump down and the name `redo` will jump up.

```-- 5.2.0-beta-rc2
::redo:: for x=1,10 do for y=1,10 do
if not f(x,y) then goto continue end
if not g(x,y) then goto skip end
if not h(x,y) then goto redo end
::continue::
end end ::skip::
```

If you use two such chunks of code in the same scope, you will need to disambiguate the label names (e.g. `@redo1:` and `@redo2:`) or wrap each in a `do/end` block.

### Scoping

Here's some examples of the scoping rules for `goto`:

```::a::
goto b  -- valid (forward jump)
goto a  -- valid (backward jump)
::b::
goto c  -- invalid (jump into nested block prohibited because nested label not even visible here)
goto d  -- invalid (jump into nested function prohibited because nested label not even visible here)
do
::c::
goto a  -- valid (backward jump out of nested block)
goto e  -- valid (forward jump out of nested block)
end
(function()
::d::
goto a  -- invalid (jump out of nested function)
end)()
do ::e:: end  -- valid, but not visible outside the block; above "goto e" sees only next line
::e::  -- valid
goto f  -- invalid (forward jump into scope of local definition)
local x
::f::
goto e  -- valid (backward jump across local definition)
--::e::  -- this would be invalid (duplicate label in same scope)
```

Note that you can think of

```do
<...>
--::a::
goto a  -- invalid (forward jump into scope of local definition)
goto b  -- valid (jump out of block)
<...>
local x
<...>
::a::
<...>
--goto a
::b::
end
```

as equivalent to

```do
<...>
--::a::
goto a  -- invalid (jump into nested block prohibited because nested label not even visible here)
goto b  -- valid (jump out of block)
<...>
do
local x
<...>
::a::
<...>
--goto a
end
::b::
end
```

so, in a way, the rule against "jump into scope of local definition" is implied by the rule against "jump into nested block" (but not vice-versa). However, 5.2.0-beta-rc1 doesn't treat scoping between these two forms exactly analogously: if you add another `::a::` before the `goto a`, the former form will generate an error about duplicate label, whereas the latter will not (though it does in rc2) because the nested `::a::` is never seen by a `goto` outside the nested block (and any `goto` inside the nested block will only see the nested `::a::`).

The particular treatment of labels at the end of the block (`::b::`) is what allows a loop continue construct to be implemented (example above) even when the loop block contains locals following the continue.

### Efficiency

`goto`'s can sometimes generate the exact same bytecodes and debuginfo as control structures, except for `for` loops:

```-- compare.lua
-- tested 5.2.0rc1

local FS = require 'file_slurp'
-- https://raw.github.com/gist/1325400/0de9b965af138f2fb3d76fc81d97a863f6f409b3/file_slurp.lua

local function compile(code)
FS.writefile('luac -o luac.out -', code, 'p')
return binary, text
end

local a, at = compile [[
local x = 1
while not(x > 1e8) do
x = x + 1
end
]]
local b, bt = compile [[
local x = 1
::a:: if x > 1e8 then goto e end
x = x + 1
goto a; ::e::
]]
assert(a == b)
assert(at == bt)

local a, at = compile [[
if x then
f()
else
g()
end
]]
local b, bt = compile [[
if not x then goto a end
f()
goto b; ::a::
g()
::b::
]]
assert(a == b)
assert(at == bt)

local a, at = compile [[
local sum = 0
for i=1,1E8 do
sum = sum + i
end
]]
local b, bt = compile [[
local sum = 0
local i=1; ::a:: if i > 1E8 then goto b end
sum = sum + i; i=i+1
goto a; ::b::
]]
assert(a ~= b) -- these differ significantly and the latter is about twice as slow.
assert(at ~= bt)

print 'DONE'
```

(In the earlier 5.2.0beta, when a single goto exists inside a conditional block, some of the JMP's were superfluous [5]).

### Changes

In 5.2.0-beta-rc1, labels used the syntax `@name:` (optionally with spaces, e.g. `@ name :`). Restrictions on duplicate label names were different.

RecentChanges · preferences
edit · history
Last edited February 2, 2014 8:27 am GMT (diff)