diff --git a/lcode.c b/lcode.c index 1005f1b7..cb6ea0dc 100644 --- a/lcode.c +++ b/lcode.c @@ -52,7 +52,7 @@ l_noret luaK_semerror (LexState *ls, const char *msg) { ** If expression is a numeric constant, fills 'v' with its value ** and returns 1. Otherwise, returns 0. */ -int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) { +static int tonumeral (const expdesc *e, TValue *v) { if (hasjumps(e)) return 0; /* not a numeral */ switch (e->k) { @@ -62,41 +62,11 @@ int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v) { case VKFLT: if (v) setfltvalue(v, e->u.nval); return 1; - case VUPVAL: { /* may be a constant */ - Vardesc *vd = luaY_getvardesc(&fs, e); - if (v && vd && !ttisnil(&vd->val)) { - setobj(fs->ls->L, v, &vd->val); - return 1; - } /* else */ - } /* FALLTHROUGH */ default: return 0; } } -/* -** If expression 'e' is a constant, change 'e' to represent -** the constant value. -*/ -static int const2exp (FuncState *fs, expdesc *e) { - Vardesc *vd = luaY_getvardesc(&fs, e); - if (vd) { - TValue *v = &vd->val; - switch (ttypetag(v)) { - case LUA_TNUMINT: - e->k = VKINT; - e->u.ival = ivalue(v); - return 1; - case LUA_TNUMFLT: - e->k = VKFLT; - e->u.nval = fltvalue(v); - return 1; - } - } - return 0; -} - - /* ** Return the previous instruction of the current code. If there ** may be a jump target between the current instruction and the @@ -708,15 +678,13 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { void luaK_dischargevars (FuncState *fs, expdesc *e) { switch (e->k) { case VLOCAL: { /* already in a register */ - e->u.info = e->u.var.idx; + e->u.info = e->u.var.sidx; e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } case VUPVAL: { /* move value to some (pending) register */ - if (!const2exp(fs, e)) { - e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0); - e->k = VRELOC; - } + e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); + e->k = VRELOC; break; } case VINDEXUP: { @@ -971,12 +939,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); - exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */ + exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */ return; } case VUPVAL: { int e = luaK_exp2anyreg(fs, ex); - luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); break; } case VINDEXUP: { @@ -1203,13 +1171,13 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ luaK_exp2anyreg(fs, t); /* put it in a register */ if (t->k == VUPVAL) { - t->u.ind.t = t->u.var.idx; /* upvalue index */ + t->u.ind.t = t->u.info; /* upvalue index */ t->u.ind.idx = k->u.info; /* literal string */ t->k = VINDEXUP; } else { /* register index of the table */ - t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info; + t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info; if (isKstr(fs, k)) { t->u.ind.idx = k->u.info; /* literal string */ t->k = VINDEXSTR; @@ -1252,9 +1220,7 @@ static int validop (int op, TValue *v1, TValue *v2) { static int constfolding (FuncState *fs, int op, expdesc *e1, const expdesc *e2) { TValue v1, v2, res; - if (!luaK_tonumeral(fs, e1, &v1) || - !luaK_tonumeral(fs, e2, &v2) || - !validop(op, &v1, &v2)) + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) return 0; /* non-numeric operands or not safe to fold */ luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ if (ttisinteger(&res)) { @@ -1341,7 +1307,7 @@ static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2, int flip, int line) { if (isSCint(e2)) /* immediate operand? */ codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line); - else if (luaK_tonumeral(fs, e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ + else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ int v2 = e2->u.info; /* K index */ op = cast(OpCode, op - OP_ADD + OP_ADDK); finishbinexpval(fs, e1, e2, op, v2, flip, line); @@ -1362,7 +1328,7 @@ static void codearith (FuncState *fs, OpCode op, static void codecommutative (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2, int line) { int flip = 0; - if (luaK_tonumeral(fs, e1, NULL)) { /* is first operand a numeric constant? */ + if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ swapexps(e1, e2); /* change order */ flip = 1; } @@ -1519,13 +1485,13 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { - if (!luaK_tonumeral(fs, v, NULL)) + if (!tonumeral(v, NULL)) luaK_exp2anyreg(fs, v); /* else keep numeral, which may be folded with 2nd operand */ break; } case OPR_EQ: case OPR_NE: { - if (!luaK_tonumeral(fs, v, NULL)) + if (!tonumeral(v, NULL)) luaK_exp2RK(fs, v); /* else keep numeral, which may be an immediate operand */ break; diff --git a/lcode.h b/lcode.h index c4953295..0758f88d 100644 --- a/lcode.h +++ b/lcode.h @@ -51,7 +51,6 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) -LUAI_FUNC int luaK_tonumeral (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, diff --git a/ldump.c b/ldump.c index c4475576..3d5b7b32 100644 --- a/ldump.c +++ b/ldump.c @@ -149,6 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) { for (i = 0; i < n; i++) { DumpByte(f->upvalues[i].instack, D); DumpByte(f->upvalues[i].idx, D); + DumpByte(f->upvalues[i].ro, D); } } diff --git a/lobject.h b/lobject.h index 403b6047..64366a94 100644 --- a/lobject.h +++ b/lobject.h @@ -460,6 +460,7 @@ typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ lu_byte instack; /* whether it is in stack (register) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ + lu_byte ro; /* true if upvalue is read-only (const) */ } Upvaldesc; diff --git a/lparser.c b/lparser.c index 52486e08..1551cda9 100644 --- a/lparser.c +++ b/lparser.c @@ -156,13 +156,6 @@ static void init_exp (expdesc *e, expkind k, int i) { } -static void init_var (expdesc *e, expkind k, int i) { - e->f = e->t = NO_JUMP; - e->k = k; - e->u.var.idx = i; -} - - static void codestring (LexState *ls, expdesc *e, TString *s) { init_exp(e, VK, luaK_stringK(ls->fs, s)); } @@ -177,16 +170,15 @@ static void codename (LexState *ls, expdesc *e) { ** Register a new local variable in the active 'Proto' (for debug ** information). */ -static int registerlocalvar (LexState *ls, TString *varname) { - FuncState *fs = ls->fs; +static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) { Proto *f = fs->f; int oldsize = f->sizelocvars; - luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + luaM_growvector(L, f->locvars, fs->nlocvars, f->sizelocvars, LocVar, SHRT_MAX, "local variables"); while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; f->locvars[fs->nlocvars].varname = varname; - luaC_objbarrier(ls->L, f, varname); + luaC_objbarrier(L, f, varname); return fs->nlocvars++; } @@ -195,18 +187,19 @@ static int registerlocalvar (LexState *ls, TString *varname) { ** Create a new local variable with the given 'name'. */ static Vardesc *new_localvar (LexState *ls, TString *name) { + lua_State *L = ls->L; FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; Vardesc *var; - int reg = registerlocalvar(ls, name); + int reg = registerlocalvar(L, fs, name); checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, MAXVARS, "local variables"); - luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, - dyd->actvar.size, Vardesc, MAX_INT, "local variables"); + 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->idx = cast(short, reg); + var->pidx = cast(short, reg); var->ro = 0; - setnilvalue(&var->val); + setnilvalue(var); return var; } @@ -223,50 +216,47 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) { return &fs->ls->dyd->actvar.arr[fs->firstlocal + i]; } + /* ** Get the debug-information entry for current variable 'i'. */ -static LocVar *getlocvar (FuncState *fs, int i) { - int idx = getlocalvardesc(fs, i)->idx; +static LocVar *localdebuginfo (FuncState *fs, int i) { + int idx = getlocalvardesc(fs, i)->pidx; lua_assert(idx < fs->nlocvars); return &fs->f->locvars[idx]; } -/* -** Return the "variable description" (Vardesc) of a given -** local variable and update 'fs' to point to the function -** where that variable was defined. Return NULL if expression -** is neither a local variable nor an upvalue. -*/ -Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e) { - if (e->k == VLOCAL) - return getlocalvardesc(*fs, e->u.var.idx); - else if (e->k != VUPVAL) - return NULL; /* not a local variable */ - else { /* upvalue: must go up all levels up to the original local */ - int idx = e->u.var.idx; - for (;;) { - Upvaldesc *up = &(*fs)->f->upvalues[idx]; - *fs = (*fs)->prev; /* must look at the previous level */ - idx = up->idx; /* at this index */ - if (*fs == NULL) /* no more levels? (can happen only with _ENV) */ - return NULL; - else if (up->instack) /* got to the original level? */ - return getlocalvardesc(*fs, idx); - /* else repeat for previous level */ - } - } +static void init_var (FuncState *fs, expdesc *e, int i) { + e->f = e->t = NO_JUMP; + e->k = VLOCAL; + e->u.var.vidx = i; + e->u.var.sidx = getlocalvardesc(fs, i)->sidx; } static void check_readonly (LexState *ls, expdesc *e) { FuncState *fs = ls->fs; - Vardesc *vardesc = luaY_getvardesc(&fs, e); - if (vardesc && vardesc->ro) { /* is variable local and const? */ + TString *varname = NULL; /* to be set if variable is const */ + switch (e->k) { + case VLOCAL: { + Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); + if (vardesc->ro) + varname = fs->f->locvars[vardesc->pidx].varname; + break; + } + case VUPVAL: { + Upvaldesc *up = &fs->f->upvalues[e->u.info]; + if (up->ro) + varname = up->name; + break; + } + default: + return; /* other cases cannot be read-only */ + } + if (varname) { const char *msg = luaO_pushfstring(ls->L, - "attempt to assign to const variable '%s'", - getstr(fs->f->locvars[vardesc->idx].varname)); + "attempt to assign to const variable '%s'", getstr(varname)); luaK_semerror(ls, msg); /* error */ } } @@ -274,13 +264,15 @@ static void check_readonly (LexState *ls, expdesc *e) { /* ** Start the scope for the last 'nvars' created variables. -** (debug info.) */ static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; - fs->nactvar = cast_byte(fs->nactvar + nvars); - for (; nvars; nvars--) { - getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc; + int i; + for (i = 0; i < nvars; i++) { + int varidx = fs->nactvar++; + Vardesc *var = getlocalvardesc(fs, varidx); + var->sidx = varidx; + fs->f->locvars[var->pidx].startpc = fs->pc; } } @@ -292,7 +284,7 @@ static void adjustlocalvars (LexState *ls, int nvars) { static void removevars (FuncState *fs, int tolevel) { fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); while (fs->nactvar > tolevel) - getlocvar(fs, --fs->nactvar)->endpc = fs->pc; + localdebuginfo(fs, --fs->nactvar)->endpc = fs->pc; } @@ -310,7 +302,7 @@ static int searchupvalue (FuncState *fs, TString *name) { } -static int newupvalue (FuncState *fs, TString *name, expdesc *v) { +static Upvaldesc *allocupvalue (FuncState *fs) { Proto *f = fs->f; int oldsize = f->sizeupvalues; checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); @@ -318,11 +310,28 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { Upvaldesc, MAXUPVAL, "upvalues"); while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; - f->upvalues[fs->nups].instack = (v->k == VLOCAL); - f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx); - f->upvalues[fs->nups].name = name; - luaC_objbarrier(fs->ls->L, f, name); - return fs->nups++; + return &f->upvalues[fs->nups++]; +} + + +static int newupvalue (FuncState *fs, TString *name, expdesc *v) { + Upvaldesc *up = allocupvalue(fs); + FuncState *prev = fs->prev; + if (v->k == VLOCAL) { + up->instack = 1; + up->idx = v->u.var.sidx; + up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro; + lua_assert(eqstr(name, localdebuginfo(prev, v->u.var.vidx)->varname)); + } + else { + up->instack = 0; + up->idx = cast_byte(v->u.info); + up->ro = prev->f->upvalues[v->u.info].ro; + lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); + } + up->name = name; + luaC_objbarrier(fs->ls->L, fs->f, name); + return fs->nups - 1; } @@ -333,7 +342,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { static int searchvar (FuncState *fs, TString *n) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { - if (eqstr(n, getlocvar(fs, i)->varname)) + if (eqstr(n, localdebuginfo(fs, i)->varname)) return i; } return -1; /* not found */ @@ -364,9 +373,9 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { else { int v = searchvar(fs, n); /* look up locals at current level */ if (v >= 0) { /* found? */ - init_var(var, VLOCAL, v); /* variable is local */ + init_var(fs, var, v); /* variable is local */ if (!base) - markupval(fs, v); /* local will be used as an upval */ + markupval(fs, var->u.var.sidx); /* local will be used as an upval */ } else { /* not found as local at current level; try upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */ @@ -377,7 +386,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { /* else was LOCAL or UPVAL */ idx = newupvalue(fs, n, var); /* will be a new upvalue */ } - init_var(var, VUPVAL, idx); /* new or old upvalue */ + init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } } @@ -440,7 +449,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { ** local variable. */ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { - const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname); + const char *varname = getstr(localdebuginfo(ls->fs, gt->nactvar)->varname); const char *msg = " at line %d jumps into the scope of local '%s'"; msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); luaK_semerror(ls, msg); /* raise the error */ @@ -1259,20 +1268,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { for (; lh; lh = lh->prev) { /* check all previous assignments */ if (vkisindexed(lh->v.k)) { /* assignment to table field? */ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ - if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) { + if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { conflict = 1; /* table is the upvalue being assigned now */ lh->v.k = VINDEXSTR; lh->v.u.ind.t = extra; /* assignment will use safe copy */ } } else { /* table is a register */ - if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) { + if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) { conflict = 1; /* table is the local being assigned now */ lh->v.u.ind.t = extra; /* assignment will use safe copy */ } /* is index the local being assigned? */ if (lh->v.k == VINDEXED && v->k == VLOCAL && - lh->v.u.ind.idx == v->u.var.idx) { + lh->v.u.ind.idx == v->u.var.sidx) { conflict = 1; lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ } @@ -1281,14 +1290,16 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { } if (conflict) { /* copy upvalue/local value to a temporary (in position 'extra') */ - OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; - luaK_codeABC(fs, op, extra, v->u.var.idx, 0); + if (v->k == VLOCAL) + luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0); + else + luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); luaK_reserveregs(fs, 1); } } /* -** Parse and compile a mulitple assignment. The first "variable" +** Parse and compile a multiple assignment. The first "variable" ** (a 'suffixedexp') was already read by the caller. ** ** assignment -> suffixedexp restassign @@ -1652,7 +1663,7 @@ static void localfunc (LexState *ls) { adjustlocalvars(ls, 1); /* enter its scope */ body(ls, &b, 0, ls->linenumber); /* function created in next register */ /* debug information will only see the variable after this point! */ - getlocvar(fs, b.u.info)->startpc = fs->pc; + localdebuginfo(fs, b.u.info)->startpc = fs->pc; } @@ -1870,11 +1881,14 @@ static void statement (LexState *ls) { */ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; - expdesc v; + Upvaldesc *env; open_func(ls, fs, &bl); setvararg(fs, 0); /* main function is always declared vararg */ - init_var(&v, VLOCAL, 0); /* create and... */ - newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ + env = allocupvalue(fs); /* ...set environment upvalue */ + env->instack = 1; + env->idx = 0; + env->ro = 0; + env->name = ls->envn; luaX_next(ls); /* read first token */ statlist(ls); /* parse main body */ check(ls, TK_EOS); diff --git a/lparser.h b/lparser.h index b708de25..cc2ec14d 100644 --- a/lparser.h +++ b/lparser.h @@ -33,8 +33,9 @@ typedef enum { VKINT, /* integer constant; nval = numerical integer value */ VNONRELOC, /* expression has its value in a fixed register; info = result register */ - VLOCAL, /* local variable; var.idx = local register */ - VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */ + VLOCAL, /* local variable; var.ridx = local register; + var.vidx = index in 'actvar.arr' */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VINDEXED, /* indexed variable; ind.t = table register; ind.idx = key's R index */ @@ -70,8 +71,9 @@ typedef struct expdesc { short idx; /* index (R or "long" K) */ lu_byte t; /* table (register or upvalue) */ } ind; - struct { /* for local variables and upvalues */ - lu_byte idx; /* index of the variable */ + struct { /* for local variables */ + lu_byte sidx; /* index in the stack */ + unsigned short vidx; /* index in 'actvar.arr' */ } var; } u; int t; /* patch list of 'exit when true' */ @@ -81,9 +83,10 @@ typedef struct expdesc { /* description of an active local variable */ typedef struct Vardesc { - TValue val; /* constant value (if variable is 'const') */ - short idx; /* index of the variable in the Proto's 'locvars' array */ + TValuefields; /* constant value (if variable is 'const') */ lu_byte ro; /* true if variable is 'const' */ + lu_byte sidx; /* index of the variable in the stack */ + short pidx; /* index of the variable in the Proto's 'locvars' array */ } Vardesc; @@ -144,7 +147,6 @@ typedef struct FuncState { } FuncState; -LUAI_FUNC Vardesc *luaY_getvardesc (FuncState **fs, const expdesc *e); LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar); diff --git a/lundump.c b/lundump.c index c1cff9e1..5c0e94d6 100644 --- a/lundump.c +++ b/lundump.c @@ -203,6 +203,7 @@ static void LoadUpvalues (LoadState *S, Proto *f) { for (i = 0; i < n; i++) { f->upvalues[i].instack = LoadByte(S); f->upvalues[i].idx = LoadByte(S); + f->upvalues[i].ro = LoadByte(S); } } diff --git a/testes/locals.lua b/testes/locals.lua index 50230a27..0de00a98 100644 --- a/testes/locals.lua +++ b/testes/locals.lua @@ -177,14 +177,24 @@ do -- constants local a, b, c = 10, 20, 30 b = a + c + b -- 'b' is not constant assert(a == 10 and b == 60 and c == 30) - local function checkro (code, name) + local function checkro (name, code) local st, msg = load(code) local gab = string.format("attempt to assign to const variable '%s'", name) assert(not st and string.find(msg, gab)) end - checkro("local x, y, z = 10, 20, 30; x = 11; y = 12", "y") - checkro("local x, y, z = 10, 20, 30; x = 11", "x") - checkro("local x, y, z = 10, 20, 30; y = 10; z = 11", "z") + checkro("y", "local x, y, z = 10, 20, 30; x = 11; y = 12") + checkro("x", "local x, y, z = 10, 20, 30; x = 11") + checkro("z", "local x, y, z = 10, 20, 30; y = 10; z = 11") + + checkro("z", [[ + local a, z, b = 10; + function foo() a = 20; z = 32; end + ]]) + + checkro("var1", [[ + local a, var1 = 10; + function foo() a = 20; z = function () var1 = 12; end end + ]]) end