Another way to compile goto's
The compilation of a goto or a label just create an entry and generate boilerplate code for the gotos. As we don't know yet whether it needs a CLOSE, we code a jump followed by a CLOSE, which is then dead code. When a block ends (and then we know for sure whether there are variables that need to be closed), we check the goto's against the labels of that block. When closing a goto against a label, if it needs a CLOSE, the compiler swaps the order of the jump and the CLOSE, making the CLOSE active.
This commit is contained in:
@@ -250,21 +250,36 @@ assert(testG(3) == "3")
|
||||
assert(testG(4) == 5)
|
||||
assert(testG(5) == 10)
|
||||
|
||||
do
|
||||
-- if x back goto out of scope of upvalue
|
||||
local X
|
||||
goto L1
|
||||
do -- test goto's around to-be-closed variable
|
||||
|
||||
::L2:: goto L3
|
||||
|
||||
::L1:: do
|
||||
local a <close> = setmetatable({}, {__close = function () X = true end})
|
||||
assert(X == nil)
|
||||
if a then goto L2 end -- jumping back out of scope of 'a'
|
||||
-- set 'var' and return an object that will reset 'var' when
|
||||
-- it goes out of scope
|
||||
local function newobj (var)
|
||||
_ENV[var] = true
|
||||
return setmetatable({}, {__close = function ()
|
||||
_ENV[var] = nil
|
||||
end})
|
||||
end
|
||||
|
||||
::L3:: assert(X == true) -- checks that 'a' was correctly closed
|
||||
goto L1
|
||||
|
||||
::L4:: assert(not X); goto L5 -- varX dead here
|
||||
|
||||
::L1::
|
||||
local varX <close> = newobj("X")
|
||||
assert(X); goto L2 -- varX alive here
|
||||
|
||||
::L3::
|
||||
assert(X); goto L4 -- varX alive here
|
||||
|
||||
::L2:: assert(X); goto L3 -- varX alive here
|
||||
|
||||
::L5:: -- return
|
||||
end
|
||||
|
||||
|
||||
|
||||
foo()
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user