Skip to content

Commit 997f11f

Browse files
committed
Bug: 'break' may not properly close variable in a 'for' loop
Function 'leaveblock' was generating "break" label before removing variables from the closing block. If 'createlabel' created a 'close' instruction (which it did when matching a goto/break that exited the scope of an upvalue), that instruction would use the wrong level.
1 parent 02060b7 commit 997f11f

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

lparser.c

+8-8
Original file line numberDiff line numberDiff line change
@@ -674,19 +674,19 @@ static void leaveblock (FuncState *fs) {
674674
LexState *ls = fs->ls;
675675
int hasclose = 0;
676676
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
677-
if (bl->isloop) /* fix pending breaks? */
677+
removevars(fs, bl->nactvar); /* remove block locals */
678+
lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
679+
if (bl->isloop) /* has to fix pending breaks? */
678680
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
679-
if (!hasclose && bl->previous && bl->upval)
681+
if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
680682
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
681-
fs->bl = bl->previous;
682-
removevars(fs, bl->nactvar);
683-
lua_assert(bl->nactvar == fs->nactvar);
684683
fs->freereg = stklevel; /* free registers */
685684
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
686-
if (bl->previous) /* inner block? */
687-
movegotosout(fs, bl); /* update pending gotos to outer block */
685+
fs->bl = bl->previous; /* current block now is previous one */
686+
if (bl->previous) /* was it a nested block? */
687+
movegotosout(fs, bl); /* update pending gotos to enclosing block */
688688
else {
689-
if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
689+
if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
690690
undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
691691
}
692692
}

testes/locals.lua

+20
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,26 @@ do
360360
end
361361

362362

363+
do
364+
-- bug in 5.4.4: 'break' may generate wrong 'close' instruction when
365+
-- leaving a loop block.
366+
367+
local closed = false
368+
369+
local o1 = setmetatable({}, {__close=function() closed = true end})
370+
371+
local function test()
372+
for k, v in next, {}, nil, o1 do
373+
local function f() return k end -- create an upvalue
374+
break
375+
end
376+
assert(closed)
377+
end
378+
379+
test()
380+
end
381+
382+
363383
do print("testing errors in __close")
364384

365385
-- original error is in __close

0 commit comments

Comments
 (0)