mirror of
https://github.com/lua/lua
synced 2024-11-25 06:09:36 +03:00
Control variables in for loops are read only
This commit is contained in:
parent
540d805226
commit
b2f7b3b79f
30
lparser.c
30
lparser.c
@ -187,10 +187,10 @@ static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
|
||||
|
||||
|
||||
/*
|
||||
** Create a new local variable with the given 'name'. Return its index
|
||||
** in the function.
|
||||
** Create a new local variable with the given 'name' and given 'kind'.
|
||||
** Return its index in the function.
|
||||
*/
|
||||
static int new_localvar (LexState *ls, TString *name) {
|
||||
static int new_localvarkind (LexState *ls, TString *name, int kind) {
|
||||
lua_State *L = ls->L;
|
||||
FuncState *fs = ls->fs;
|
||||
Dyndata *dyd = ls->dyd;
|
||||
@ -200,11 +200,19 @@ static int new_localvar (LexState *ls, TString *name) {
|
||||
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
|
||||
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||
var->vd.kind = VDKREG; /* default */
|
||||
var->vd.kind = kind; /* default */
|
||||
var->vd.name = name;
|
||||
return dyd->actvar.n - 1 - fs->firstlocal;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a new local variable with the given 'name' and regular kind.
|
||||
*/
|
||||
static int new_localvar (LexState *ls, TString *name) {
|
||||
return new_localvarkind(ls, name, VDKREG);
|
||||
}
|
||||
|
||||
#define new_localvarliteral(ls,v) \
|
||||
new_localvar(ls, \
|
||||
luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
||||
@ -1573,7 +1581,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvar(ls, varname);
|
||||
new_localvarkind(ls, varname, RDKCONST); /* control variable */
|
||||
checknext(ls, '=');
|
||||
exp1(ls); /* initial value */
|
||||
checknext(ls, ',');
|
||||
@ -1601,8 +1609,8 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
/* create declared variables */
|
||||
new_localvar(ls, indexname);
|
||||
new_localvarkind(ls, indexname, RDKCONST); /* control variable */
|
||||
/* other declared variables */
|
||||
while (testnext(ls, ',')) {
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
nvars++;
|
||||
@ -1728,14 +1736,14 @@ static void localstat (LexState *ls) {
|
||||
FuncState *fs = ls->fs;
|
||||
int toclose = -1; /* index of to-be-closed variable (if any) */
|
||||
Vardesc *var; /* last variable */
|
||||
int vidx, kind; /* index and kind of last variable */
|
||||
int vidx; /* index of last variable */
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
do {
|
||||
vidx = new_localvar(ls, str_checkname(ls));
|
||||
kind = getlocalattribute(ls);
|
||||
getlocalvardesc(fs, vidx)->vd.kind = kind;
|
||||
TString *vname = str_checkname(ls);
|
||||
int kind = getlocalattribute(ls);
|
||||
vidx = new_localvarkind(ls, vname, kind);
|
||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||
|
@ -1467,7 +1467,7 @@ It has the following syntax:
|
||||
exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
|
||||
}
|
||||
The given identifier (@bnfNter{Name}) defines the control variable,
|
||||
which is a new variable local to the loop body (@emph{block}).
|
||||
which is a new read-only variable local to the loop body (@emph{block}).
|
||||
|
||||
The loop starts by evaluating once the three control expressions.
|
||||
Their values are called respectively
|
||||
@ -1499,11 +1499,6 @@ For integer loops,
|
||||
the control variable never wraps around;
|
||||
instead, the loop ends in case of an overflow.
|
||||
|
||||
You should not change the value of the control variable
|
||||
during the loop.
|
||||
If you need its value after the loop,
|
||||
assign it to another variable before exiting the loop.
|
||||
|
||||
}
|
||||
|
||||
@sect4{@title{The generic @Rw{for} loop}
|
||||
@ -1526,7 +1521,8 @@ for @rep{var_1}, @Cdots, @rep{var_n} in @rep{explist} do @rep{body} end
|
||||
works as follows.
|
||||
|
||||
The names @rep{var_i} declare loop variables local to the loop body.
|
||||
The first of these variables is the @emph{control variable}.
|
||||
The first of these variables is the @emph{control variable},
|
||||
which is a read-only variable.
|
||||
|
||||
The loop starts by evaluating @rep{explist}
|
||||
to produce four values:
|
||||
@ -1550,9 +1546,6 @@ to-be-closed variable @see{to-be-closed},
|
||||
which can be used to release resources when the loop ends.
|
||||
Otherwise, it does not interfere with the loop.
|
||||
|
||||
You should not change the value of the control variable
|
||||
during the loop.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -9156,6 +9149,9 @@ change between versions.
|
||||
@itemize{
|
||||
|
||||
@item{
|
||||
The control variable in @Rw{for} loops are read only.
|
||||
If you need to change it,
|
||||
declare a local variable with the same name in the loop body.
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ package.path = oldpath
|
||||
local fname = "file_does_not_exist2"
|
||||
local m, err = pcall(require, fname)
|
||||
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
|
||||
t = string.gsub(t, "?", fname)
|
||||
local t = string.gsub(t, "?", fname)
|
||||
assert(string.find(err, t, 1, true))
|
||||
end
|
||||
|
||||
|
@ -60,32 +60,29 @@ end
|
||||
-- testing closures with 'for' control variable
|
||||
a = {}
|
||||
for i=1,10 do
|
||||
a[i] = {set = function(x) i=x end, get = function () return i end}
|
||||
a[i] = function () return i end
|
||||
if i == 3 then break end
|
||||
end
|
||||
assert(a[4] == undef)
|
||||
a[1].set(10)
|
||||
assert(a[2].get() == 2)
|
||||
a[2].set('a')
|
||||
assert(a[3].get() == 3)
|
||||
assert(a[2].get() == 'a')
|
||||
assert(a[2]() == 2)
|
||||
assert(a[3]() == 3)
|
||||
|
||||
a = {}
|
||||
local t = {"a", "b"}
|
||||
for i = 1, #t do
|
||||
local k = t[i]
|
||||
a[i] = {set = function(x, y) i=x; k=y end,
|
||||
a[i] = {set = function(x) k=x end,
|
||||
get = function () return i, k end}
|
||||
if i == 2 then break end
|
||||
end
|
||||
a[1].set(10, 20)
|
||||
a[1].set(10)
|
||||
local r,s = a[2].get()
|
||||
assert(r == 2 and s == 'b')
|
||||
r,s = a[1].get()
|
||||
assert(r == 10 and s == 20)
|
||||
a[2].set('a', 'b')
|
||||
assert(r == 1 and s == 10)
|
||||
a[2].set('a')
|
||||
r,s = a[2].get()
|
||||
assert(r == "a" and s == "b")
|
||||
assert(r == 2 and s == "a")
|
||||
|
||||
|
||||
-- testing closures with 'for' control variable x break
|
||||
|
@ -609,10 +609,12 @@ do
|
||||
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
|
||||
end
|
||||
|
||||
do -- changing the control variable
|
||||
local a
|
||||
a = 0; for i = 1, 10 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
a = 0; for i = 10.0, 1, -1 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
do -- attempt to change the control variable
|
||||
local st, msg = load "for i = 1, 10 do i = 10 end"
|
||||
assert(not st and string.find(msg, "assign to const variable 'i'"))
|
||||
|
||||
local st, msg = load "for v, k in pairs{} do v = 10 end"
|
||||
assert(not st and string.find(msg, "assign to const variable 'v'"))
|
||||
end
|
||||
|
||||
-- conversion
|
||||
|
Loading…
Reference in New Issue
Block a user