[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: goto jumps into scope of local (Lua 5.2.0-beta)
- From: David Manura <dm.lua@...>
- Date: Sat, 2 Jul 2011 19:40:24 -0400
In Lua 5.2.0-beta-rc4, gotos allow transactional error handling code
to be written something like this:
-- 5.2.0-beta-rc2
function perform_transaction()
if not f() then goto undone_f end -- [*2]
if not g() then goto undone_g end -- [*2]
local hval = h()
if not hval then goto undone_h end -- [*2]
local x
.....
return true -- success [*1]
-- rollback
::undone_h::
undo_g() ::undone_g::
undo_f() ::undone_f::
print(x)
return false -- failure
end
except for two issues. First, the "return true" is required to be
wrapped in do/end [*1]. Secondly, and more importantly, the gotos
will be rejected as written because they jump into the scope of locals
[*2] ....or do they? The reference manual narrows variable scope
slightly:
"The scope of a local variable begins at the first statement after
its declaration and lasts until the last non-void statement of the
innermost block that includes the declaration."
thereby permitting things like continue: `whlie ..... goto continue
..... local x ..... ::continue:: end`. It would be possible though to
tighten the scope rules further to allow the error handling code
above. The new rule would be that the scope of a local ends at a
label that is jumped to from outside that variable's scope.
Therefore, the `print(x)` above will print nil.
However, the Lua 5.1 way of doing all this may still be easier to understand:
function perform_transaction()
local fval, gval, hval
repeat
fval = f(); if not fval then break end
gval = g(); if not gval then break end
hval = h(); if not hval then break end -- alternately `goto`
the precise rollback statement below
local x
.....
return true
until false -- an abuse of repeat; alternately use `do ..... goto
last ..... end ::last::`
-- rollback
if gval then undo_g() end -- alternately add labels here to jump to
if fval then undo_f() end
return false -- failure
end
There are some redundant conditionals in the rollback code, which can
now be eliminated with gotos again, but as with the case with
exception handling, we can normally assume that traversal of the error
handling code path is rare and not performance critical.
Now, more ideally, a macro preprocessor could transform the below code
into the above:
function perform_transaction()
ROLLBACK.FAIL return false
CHECK.FAIL f(); ROLLBACK.FAIL undo_f()
CHECK.FAIL g(); ROLLBACK.FAIL undo_g()
hval = h(); CHECK.FAIL hval
local x
.....
return true
end
The idea is that the ROLLBACK prefixed statements are normally
ignored. However, if the conditional in a CHECK.X statement is false,
then any ROLLBACK.X statements above it are immediately executed in
reverse order. To take another example, this:
function read(filename)
ROLLBACK.FAIL return nil, err
local fh, err = io.open(filename); CHECK.FAIL fh; ROLLBACK.ALWAYS fh:close()
return fh:read()
end
would be translated into
function read(filename)
local fh, err = io.open(filename)
-- CHECK.FAIL fh
if not(fh) then goto fail1 end
-- return fh:read(), part 1 of 2
local _tmp1 = return fh:read()
-- ROLLBACK.ALWAYS
fh:close()
-- return fh:read(), part 2 of 2
return _tmp1
::fail1::
-- ROLLBACK.FAIL
return nil, err
end