Skip to content

Commit 63295f1

Browse files
committed
Fixed two bugs in to-be-closed variables x constants
The parser were mixing compiler indices of variables with stack indices, so that when a to-be-closed variable was used inside the scope of compile-time constants (which may be optimized away), it might be closed in the wrong place. (See new tests for examples.) Besides fixing the bugs, this commit also changed comments and variable names to avoid that kind of confusion and added tests.
1 parent 50523b1 commit 63295f1

3 files changed

Lines changed: 75 additions & 30 deletions

File tree

lparser.c

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -212,27 +212,28 @@ static int new_localvar (LexState *ls, TString *name) {
212212

213213

214214
/*
215-
** Return the "variable description" (Vardesc) of a given
216-
** variable
215+
** Return the "variable description" (Vardesc) of a given variable.
216+
** (Unless noted otherwise, all variables are referred to by their
217+
** compiler indices.)
217218
*/
218-
static Vardesc *getlocalvardesc (FuncState *fs, int i) {
219-
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
219+
static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
220+
return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx];
220221
}
221222

222223

223224
/*
224-
** Convert 'nvar' (number of active variables at some point) to
225-
** number of variables in the stack at that point.
225+
** Convert 'nvar', a compiler index level, to it corresponding
226+
** stack index level. For that, search for the highest variable
227+
** below that level that is in the stack and uses its stack
228+
** index ('sidx').
226229
*/
227230
static int stacklevel (FuncState *fs, int nvar) {
228-
while (nvar > 0) {
229-
Vardesc *vd = getlocalvardesc(fs, nvar - 1);
231+
while (nvar-- > 0) {
232+
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
230233
if (vd->vd.kind != RDKCTC) /* is in the stack? */
231234
return vd->vd.sidx + 1;
232-
else
233-
nvar--; /* try previous variable */
234235
}
235-
return 0; /* no variables */
236+
return 0; /* no variables in the stack */
236237
}
237238

238239

@@ -245,10 +246,10 @@ int luaY_nvarstack (FuncState *fs) {
245246

246247

247248
/*
248-
** Get the debug-information entry for current variable 'i'.
249+
** Get the debug-information entry for current variable 'vidx'.
249250
*/
250-
static LocVar *localdebuginfo (FuncState *fs, int i) {
251-
Vardesc *vd = getlocalvardesc(fs, i);
251+
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
252+
Vardesc *vd = getlocalvardesc(fs, vidx);
252253
if (vd->vd.kind == RDKCTC)
253254
return NULL; /* no debug info. for constants */
254255
else {
@@ -259,14 +260,20 @@ static LocVar *localdebuginfo (FuncState *fs, int i) {
259260
}
260261

261262

262-
static void init_var (FuncState *fs, expdesc *e, int i) {
263+
/*
264+
** Create an expression representing variable 'vidx'
265+
*/
266+
static void init_var (FuncState *fs, expdesc *e, int vidx) {
263267
e->f = e->t = NO_JUMP;
264268
e->k = VLOCAL;
265-
e->u.var.vidx = i;
266-
e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
269+
e->u.var.vidx = vidx;
270+
e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
267271
}
268272

269273

274+
/*
275+
** Raises an error if variable described by 'e' is read only
276+
*/
270277
static void check_readonly (LexState *ls, expdesc *e) {
271278
FuncState *fs = ls->fs;
272279
TString *varname = NULL; /* to be set if variable is const */
@@ -306,8 +313,8 @@ static void adjustlocalvars (LexState *ls, int nvars) {
306313
int stklevel = luaY_nvarstack(fs);
307314
int i;
308315
for (i = 0; i < nvars; i++) {
309-
int varidx = fs->nactvar++;
310-
Vardesc *var = getlocalvardesc(fs, varidx);
316+
int vidx = fs->nactvar++;
317+
Vardesc *var = getlocalvardesc(fs, vidx);
311318
var->vd.sidx = stklevel++;
312319
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
313320
}
@@ -377,7 +384,8 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
377384

378385
/*
379386
** Look for an active local variable with the name 'n' in the
380-
** function 'fs'.
387+
** function 'fs'. If found, initialize 'var' with it and return
388+
** its expression kind; otherwise return -1.
381389
*/
382390
static int searchvar (FuncState *fs, TString *n, expdesc *var) {
383391
int i;
@@ -1592,7 +1600,7 @@ static void forlist (LexState *ls, TString *indexname) {
15921600
line = ls->linenumber;
15931601
adjust_assign(ls, 4, explist(ls, &e), &e);
15941602
adjustlocalvars(ls, 4); /* control variables */
1595-
markupval(fs, luaY_nvarstack(fs)); /* state may create an upvalue */
1603+
markupval(fs, fs->nactvar); /* last control var. must be closed */
15961604
luaK_checkstack(fs, 3); /* extra space to call generator */
15971605
forbody(ls, base, line, nvars - 4, 1);
15981606
}
@@ -1730,7 +1738,7 @@ static int getlocalattribute (LexState *ls) {
17301738
luaK_semerror(ls,
17311739
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
17321740
}
1733-
return VDKREG;
1741+
return VDKREG; /* regular variable */
17341742
}
17351743

17361744

@@ -1739,7 +1747,7 @@ static void checktoclose (LexState *ls, int level) {
17391747
FuncState *fs = ls->fs;
17401748
markupval(fs, level + 1);
17411749
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
1742-
luaK_codeABC(fs, OP_TBC, level, 0, 0);
1750+
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
17431751
}
17441752
}
17451753

@@ -1749,18 +1757,18 @@ static void localstat (LexState *ls) {
17491757
FuncState *fs = ls->fs;
17501758
int toclose = -1; /* index of to-be-closed variable (if any) */
17511759
Vardesc *var; /* last variable */
1752-
int ivar, kind; /* index and kind of last variable */
1760+
int vidx, kind; /* index and kind of last variable */
17531761
int nvars = 0;
17541762
int nexps;
17551763
expdesc e;
17561764
do {
1757-
ivar = new_localvar(ls, str_checkname(ls));
1765+
vidx = new_localvar(ls, str_checkname(ls));
17581766
kind = getlocalattribute(ls);
1759-
getlocalvardesc(fs, ivar)->vd.kind = kind;
1767+
getlocalvardesc(fs, vidx)->vd.kind = kind;
17601768
if (kind == RDKTOCLOSE) { /* to-be-closed? */
17611769
if (toclose != -1) /* one already present? */
17621770
luaK_semerror(ls, "multiple to-be-closed variables in local list");
1763-
toclose = luaY_nvarstack(fs) + nvars;
1771+
toclose = fs->nactvar + nvars;
17641772
}
17651773
nvars++;
17661774
} while (testnext(ls, ','));
@@ -1770,7 +1778,7 @@ static void localstat (LexState *ls) {
17701778
e.k = VVOID;
17711779
nexps = 0;
17721780
}
1773-
var = getlocalvardesc(fs, ivar); /* get last variable */
1781+
var = getlocalvardesc(fs, vidx); /* get last variable */
17741782
if (nvars == nexps && /* no adjustments? */
17751783
var->vd.kind == RDKCONST && /* last variable is const? */
17761784
luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */

lparser.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ typedef struct expdesc {
7777
} ind;
7878
struct { /* for local variables */
7979
lu_byte sidx; /* index in the stack */
80-
unsigned short vidx; /* index in 'actvar.arr' */
80+
unsigned short vidx; /* compiler index (in 'actvar.arr') */
8181
} var;
8282
} u;
8383
int t; /* patch list of 'exit when true' */
@@ -125,7 +125,7 @@ typedef struct Labellist {
125125

126126
/* dynamic structures used by the parser */
127127
typedef struct Dyndata {
128-
struct { /* list of active local variables */
128+
struct { /* list of all active local variables */
129129
Vardesc *arr;
130130
int n;
131131
int size;

testes/locals.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,43 @@ do
264264
end
265265

266266

267+
-- testing to-be-closed x compile-time constants
268+
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
269+
-- between compile levels and stack levels of variables)
270+
do
271+
local flag = false
272+
local x = setmetatable({},
273+
{__close = function() assert(flag == false); flag = true end})
274+
local y <const> = nil
275+
local z <const> = nil
276+
do
277+
local a <close> = x
278+
end
279+
assert(flag) -- 'x' must be closed here
280+
end
281+
282+
do
283+
-- similar problem, but with implicit close in for loops
284+
local flag = false
285+
local x = setmetatable({},
286+
{__close = function () assert(flag == false); flag = true end})
287+
-- return an empty iterator, nil, nil, and 'x' to be closed
288+
local function a ()
289+
return (function () return nil end), nil, nil, x
290+
end
291+
local v <const> = 1
292+
local w <const> = 1
293+
local x <const> = 1
294+
local y <const> = 1
295+
local z <const> = 1
296+
for k in a() do
297+
a = k
298+
end -- ending the loop must close 'x'
299+
assert(flag) -- 'x' must be closed here
300+
end
301+
302+
303+
267304
do
268305
-- calls cannot be tail in the scope of to-be-closed variables
269306
local X, Y

0 commit comments

Comments
 (0)