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:
Roberto Ierusalimschy
2025-01-10 13:54:51 -03:00
parent 8a3a49250c
commit 7ca3c40b50
6 changed files with 120 additions and 122 deletions

View File

@@ -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()
--------------------------------------------------------------------------------