Control variables in for loops are read only
This commit is contained in:
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
|
** Create a new local variable with the given 'name' and given 'kind'.
|
||||||
** in the function.
|
** 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;
|
lua_State *L = ls->L;
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
Dyndata *dyd = ls->dyd;
|
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,
|
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||||
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
|
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
|
||||||
var = &dyd->actvar.arr[dyd->actvar.n++];
|
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||||
var->vd.kind = VDKREG; /* default */
|
var->vd.kind = kind; /* default */
|
||||||
var->vd.name = name;
|
var->vd.name = name;
|
||||||
return dyd->actvar.n - 1 - fs->firstlocal;
|
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) \
|
#define new_localvarliteral(ls,v) \
|
||||||
new_localvar(ls, \
|
new_localvar(ls, \
|
||||||
luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
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_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, '=');
|
checknext(ls, '=');
|
||||||
exp1(ls); /* initial value */
|
exp1(ls); /* initial value */
|
||||||
checknext(ls, ',');
|
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)");
|
new_localvarliteral(ls, "(for state)");
|
||||||
new_localvarliteral(ls, "(for state)");
|
new_localvarliteral(ls, "(for state)");
|
||||||
/* create declared variables */
|
new_localvarkind(ls, indexname, RDKCONST); /* control variable */
|
||||||
new_localvar(ls, indexname);
|
/* other declared variables */
|
||||||
while (testnext(ls, ',')) {
|
while (testnext(ls, ',')) {
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
nvars++;
|
nvars++;
|
||||||
@@ -1728,14 +1736,14 @@ static void localstat (LexState *ls) {
|
|||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
int toclose = -1; /* index of to-be-closed variable (if any) */
|
int toclose = -1; /* index of to-be-closed variable (if any) */
|
||||||
Vardesc *var; /* last variable */
|
Vardesc *var; /* last variable */
|
||||||
int vidx, kind; /* index and kind of last variable */
|
int vidx; /* index of last variable */
|
||||||
int nvars = 0;
|
int nvars = 0;
|
||||||
int nexps;
|
int nexps;
|
||||||
expdesc e;
|
expdesc e;
|
||||||
do {
|
do {
|
||||||
vidx = new_localvar(ls, str_checkname(ls));
|
TString *vname = str_checkname(ls);
|
||||||
kind = getlocalattribute(ls);
|
int kind = getlocalattribute(ls);
|
||||||
getlocalvardesc(fs, vidx)->vd.kind = kind;
|
vidx = new_localvarkind(ls, vname, kind);
|
||||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||||
if (toclose != -1) /* one already present? */
|
if (toclose != -1) /* one already present? */
|
||||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
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}}
|
exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
|
||||||
}
|
}
|
||||||
The given identifier (@bnfNter{Name}) defines the control variable,
|
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.
|
The loop starts by evaluating once the three control expressions.
|
||||||
Their values are called respectively
|
Their values are called respectively
|
||||||
@@ -1499,11 +1499,6 @@ For integer loops,
|
|||||||
the control variable never wraps around;
|
the control variable never wraps around;
|
||||||
instead, the loop ends in case of an overflow.
|
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}
|
@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.
|
works as follows.
|
||||||
|
|
||||||
The names @rep{var_i} declare loop variables local to the loop body.
|
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}
|
The loop starts by evaluating @rep{explist}
|
||||||
to produce four values:
|
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.
|
which can be used to release resources when the loop ends.
|
||||||
Otherwise, it does not interfere with the loop.
|
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{
|
@itemize{
|
||||||
|
|
||||||
@item{
|
@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 fname = "file_does_not_exist2"
|
||||||
local m, err = pcall(require, fname)
|
local m, err = pcall(require, fname)
|
||||||
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
|
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))
|
assert(string.find(err, t, 1, true))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -60,32 +60,29 @@ end
|
|||||||
-- testing closures with 'for' control variable
|
-- testing closures with 'for' control variable
|
||||||
a = {}
|
a = {}
|
||||||
for i=1,10 do
|
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
|
if i == 3 then break end
|
||||||
end
|
end
|
||||||
assert(a[4] == undef)
|
assert(a[4] == undef)
|
||||||
a[1].set(10)
|
assert(a[2]() == 2)
|
||||||
assert(a[2].get() == 2)
|
assert(a[3]() == 3)
|
||||||
a[2].set('a')
|
|
||||||
assert(a[3].get() == 3)
|
|
||||||
assert(a[2].get() == 'a')
|
|
||||||
|
|
||||||
a = {}
|
a = {}
|
||||||
local t = {"a", "b"}
|
local t = {"a", "b"}
|
||||||
for i = 1, #t do
|
for i = 1, #t do
|
||||||
local k = t[i]
|
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}
|
get = function () return i, k end}
|
||||||
if i == 2 then break end
|
if i == 2 then break end
|
||||||
end
|
end
|
||||||
a[1].set(10, 20)
|
a[1].set(10)
|
||||||
local r,s = a[2].get()
|
local r,s = a[2].get()
|
||||||
assert(r == 2 and s == 'b')
|
assert(r == 2 and s == 'b')
|
||||||
r,s = a[1].get()
|
r,s = a[1].get()
|
||||||
assert(r == 10 and s == 20)
|
assert(r == 1 and s == 10)
|
||||||
a[2].set('a', 'b')
|
a[2].set('a')
|
||||||
r,s = a[2].get()
|
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
|
-- 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)
|
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- changing the control variable
|
do -- attempt to change the control variable
|
||||||
local a
|
local st, msg = load "for i = 1, 10 do i = 10 end"
|
||||||
a = 0; for i = 1, 10 do a = a + 1; i = "x" end; assert(a == 10)
|
assert(not st and string.find(msg, "assign to const variable 'i'"))
|
||||||
a = 0; for i = 10.0, 1, -1 do a = a + 1; i = "x" end; assert(a == 10)
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
-- conversion
|
-- conversion
|
||||||
|
|||||||
Reference in New Issue
Block a user