aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-04 15:01:42 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-04 15:01:42 -0200
commit28d829c86712ce5bc453feccc5129a32f78d80c0 (patch)
tree58197e763ca643bbe4a042372927bf81092b141c
parent6d04537ea660fd12fc16c328366b701fabaf4919 (diff)
downloadlua-28d829c86712ce5bc453feccc5129a32f78d80c0.tar.gz
lua-28d829c86712ce5bc453feccc5129a32f78d80c0.tar.bz2
lua-28d829c86712ce5bc453feccc5129a32f78d80c0.zip
Calls cannot be tail in the scope of a to-be-closed variable
A to-be-closed variable must be closed when a block ends, so even a 'return foo()' cannot directly returns the results of 'foo'; the function must close the scope before returning.
-rw-r--r--lparser.c5
-rw-r--r--lvm.c2
-rw-r--r--manual/manual.of17
-rw-r--r--testes/locals.lua13
4 files changed, 21 insertions, 16 deletions
diff --git a/lparser.c b/lparser.c
index e525e578..eed8bffd 100644
--- a/lparser.c
+++ b/lparser.c
@@ -53,6 +53,7 @@ typedef struct BlockCnt {
53 lu_byte nactvar; /* # active locals outside the block */ 53 lu_byte nactvar; /* # active locals outside the block */
54 lu_byte upval; /* true if some variable in the block is an upvalue */ 54 lu_byte upval; /* true if some variable in the block is an upvalue */
55 lu_byte isloop; /* true if 'block' is a loop */ 55 lu_byte isloop; /* true if 'block' is a loop */
56 lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
56} BlockCnt; 57} BlockCnt;
57 58
58 59
@@ -510,6 +511,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
510 bl->firstlabel = fs->ls->dyd->label.n; 511 bl->firstlabel = fs->ls->dyd->label.n;
511 bl->firstgoto = fs->ls->dyd->gt.n; 512 bl->firstgoto = fs->ls->dyd->gt.n;
512 bl->upval = 0; 513 bl->upval = 0;
514 bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
513 bl->previous = fs->bl; 515 bl->previous = fs->bl;
514 fs->bl = bl; 516 fs->bl = bl;
515 lua_assert(fs->freereg == fs->nactvar); 517 lua_assert(fs->freereg == fs->nactvar);
@@ -1631,6 +1633,7 @@ static void tocloselocalstat (LexState *ls) {
1631 checknext(ls, '='); 1633 checknext(ls, '=');
1632 exp1(ls, 0); 1634 exp1(ls, 0);
1633 markupval(fs, fs->nactvar); 1635 markupval(fs, fs->nactvar);
1636 fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
1634 adjustlocalvars(ls, 1); 1637 adjustlocalvars(ls, 1);
1635 luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0); 1638 luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
1636} 1639}
@@ -1701,7 +1704,7 @@ static void retstat (LexState *ls) {
1701 nret = explist(ls, &e); /* optional return values */ 1704 nret = explist(ls, &e); /* optional return values */
1702 if (hasmultret(e.k)) { 1705 if (hasmultret(e.k)) {
1703 luaK_setmultret(fs, &e); 1706 luaK_setmultret(fs, &e);
1704 if (e.k == VCALL && nret == 1) { /* tail call? */ 1707 if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
1705 SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); 1708 SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
1706 lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar); 1709 lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
1707 } 1710 }
diff --git a/lvm.c b/lvm.c
index 50d967c6..fc8722a8 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1565,7 +1565,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1565 if (nparams1) /* vararg function? */ 1565 if (nparams1) /* vararg function? */
1566 delta = ci->u.l.nextraargs + nparams1; 1566 delta = ci->u.l.nextraargs + nparams1;
1567 /* close upvalues from current call */ 1567 /* close upvalues from current call */
1568 ProtectNT(luaF_close(L, base, LUA_OK)); 1568 luaF_close(L, base, -1); /* (no to-be-closed vars. here) */
1569 updatestack(ci); 1569 updatestack(ci);
1570 } 1570 }
1571 if (!ttisfunction(s2v(ra))) { /* not a function? */ 1571 if (!ttisfunction(s2v(ra))) { /* not a function? */
diff --git a/manual/manual.of b/manual/manual.of
index 3902f2f3..8b5e5d93 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -1538,9 +1538,6 @@ except that its value is @emph{closed} whenever the variable
1538goes out of scope, including normal block termination, 1538goes out of scope, including normal block termination,
1539exiting its block by @Rw{break}/@Rw{goto}/@Rw{return}, 1539exiting its block by @Rw{break}/@Rw{goto}/@Rw{return},
1540or exiting by an error. 1540or exiting by an error.
1541If a block ends in a tail call @see{functioncall},
1542all variables of the caller function go out of scope
1543before the start of the callee function.
1544 1541
1545To \emph{close} a value has the following meaning here: 1542To \emph{close} a value has the following meaning here:
1546If the value of the variable when it goes out of scope is a function, 1543If the value of the variable when it goes out of scope is a function,
@@ -2038,8 +2035,8 @@ A call of the form @T{f'@rep{string}'}
2038is syntactic sugar for @T{f('@rep{string}')}; 2035is syntactic sugar for @T{f('@rep{string}')};
2039that is, the argument list is a single literal string. 2036that is, the argument list is a single literal string.
2040 2037
2041A call of the form @T{return @rep{functioncall}} is called 2038A call of the form @T{return @rep{functioncall}} not in the
2042a @def{tail call}. 2039scope of a to-be-closed variable is called a @def{tail call}.
2043Lua implements @def{proper tail calls} 2040Lua implements @def{proper tail calls}
2044(or @emph{proper tail recursion}): 2041(or @emph{proper tail recursion}):
2045in a tail call, 2042in a tail call,
@@ -2049,13 +2046,15 @@ a program can execute.
2049However, a tail call erases any debug information about the 2046However, a tail call erases any debug information about the
2050calling function. 2047calling function.
2051Note that a tail call only happens with a particular syntax, 2048Note that a tail call only happens with a particular syntax,
2052where the @Rw{return} has one single function call as argument; 2049where the @Rw{return} has one single function call as argument,
2053this syntax makes the calling function return exactly 2050and it is outside the scope of any to-be-closed variable.
2054the returns of the called function. 2051This syntax makes the calling function return exactly
2052the returns of the called function,
2053without any intervening action.
2055So, none of the following examples are tail calls: 2054So, none of the following examples are tail calls:
2056@verbatim{ 2055@verbatim{
2057return (f(x)) -- results adjusted to 1 2056return (f(x)) -- results adjusted to 1
2058return 2 * f(x) 2057return 2 * f(x) -- result multiplied by 2
2059return x, f(x) -- additional results 2058return x, f(x) -- additional results
2060f(x); return -- results discarded 2059f(x); return -- results discarded
2061return x or f(x) -- results adjusted to 1 2060return x or f(x) -- results adjusted to 1
diff --git a/testes/locals.lua b/testes/locals.lua
index 24681dd9..340af61c 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -225,21 +225,24 @@ end
225 225
226 226
227do 227do
228 -- to-be-closed variables must be closed in tail calls 228 -- calls cannot be tail in the scope of to-be-closed variables
229 local X, Y 229 local X, Y
230 local function foo () 230 local function foo ()
231 local *toclose _ = function () Y = 10 end 231 local *toclose _ = function () Y = 10 end
232 assert(X == 20 and Y == nil) 232 assert(X == true and Y == nil) -- 'X' not closed yet
233 return 1,2,3 233 return 1,2,3
234 end 234 end
235 235
236 local function bar () 236 local function bar ()
237 local *toclose _ = function () X = 20 end 237 local *toclose _ = function () X = false end
238 return foo() 238 X = true
239 do
240 return foo() -- not a tail call!
241 end
239 end 242 end
240 243
241 local a, b, c, d = bar() 244 local a, b, c, d = bar()
242 assert(a == 1 and b == 2 and c == 3 and X == 20 and Y == 10 and d == nil) 245 assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil)
243end 246end
244 247
245 248