Bug: tbc variables in "for" loops don't avoid tail calls

This commit is contained in:
Roberto Ierusalimschy 2021-04-07 14:59:26 -03:00
parent 36de01d988
commit 47cffdc723
2 changed files with 38 additions and 6 deletions

View File

@ -416,6 +416,17 @@ static void markupval (FuncState *fs, int level) {
}
/*
** Mark that current block has a to-be-closed variable.
*/
static void marktobeclosed (FuncState *fs) {
BlockCnt *bl = fs->bl;
bl->upval = 1;
bl->insidetbc = 1;
fs->needclose = 1;
}
/*
** Find a variable with the given name 'n'. If it is an upvalue, add
** this upvalue into all intermediate functions. If it is a global, set
@ -1599,7 +1610,7 @@ static void forlist (LexState *ls, TString *indexname) {
line = ls->linenumber;
adjust_assign(ls, 4, explist(ls, &e), &e);
adjustlocalvars(ls, 4); /* control variables */
markupval(fs, fs->nactvar); /* last control var. must be closed */
marktobeclosed(fs); /* last control var. must be closed */
luaK_checkstack(fs, 3); /* extra space to call generator */
forbody(ls, base, line, nvars - 4, 1);
}
@ -1703,11 +1714,9 @@ static int getlocalattribute (LexState *ls) {
}
static void checktoclose (LexState *ls, int level) {
static void checktoclose (FuncState *fs, int level) {
if (level != -1) { /* is there a to-be-closed variable? */
FuncState *fs = ls->fs;
markupval(fs, level + 1);
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
marktobeclosed(fs);
luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
}
}
@ -1751,7 +1760,7 @@ static void localstat (LexState *ls) {
adjust_assign(ls, nvars, nexps, &e);
adjustlocalvars(ls, nvars);
}
checktoclose(ls, toclose);
checktoclose(fs, toclose);
}

View File

@ -335,6 +335,29 @@ do
end
do
-- bug in 5.4.3: previous condition (calls cannot be tail in the
-- scope of to-be-closed variables) must be valid for tbc variables
-- created by 'for' loops.
local closed = false
local function foo ()
return function () return true end, 0, 0,
func2close(function () closed = true end)
end
local function tail() return closed end
local function foo1 ()
for k in foo() do return tail() end
end
assert(foo1() == false)
assert(closed == true)
end
do print("testing errors in __close")
-- original error is in __close