lua

A copy of the Lua development repository
Log | Files | Refs | README

commit 7d526e75a7f45a2593e874d97c7fdfa0e45cc013
parent c12983cf8afac4c4c757b84aaddab1935a931641
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date:   Mon, 28 Oct 2019 15:57:40 -0300

Fixed bug in tail calls of __call chains

A tail call of a __call chain (a __call metamethod that itself is
also not a function) was being perfomed as a regular call.

Diffstat:
Mlvm.c | 3++-
Mtestes/calls.lua | 25++++++++++++++++++++++++-
2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/lvm.c b/lvm.c @@ -1549,9 +1549,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) { luaF_close(L, base, NOCLOSINGMETH); lua_assert(base == ci->func + 1); } - if (!ttisfunction(s2v(ra))) { /* not a function? */ + while (!ttisfunction(s2v(ra))) { /* not a function? */ luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ b++; /* there is now one extra argument */ + checkstackp(L, 1, ra); } if (!ttisLclosure(s2v(ra))) { /* C function? */ luaD_call(L, ra, LUA_MULTRET); /* call it */ diff --git a/testes/calls.lua b/testes/calls.lua @@ -107,7 +107,9 @@ end deep(10) deep(180) --- testing tail calls + +print"testing tail calls" + function deep (n) if n>0 then return deep(n-1) else return 101 end end assert(deep(30000) == 101) a = {} @@ -148,6 +150,27 @@ do -- tail calls x varargs assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30) end + + +do -- tail calls x chain of __call + local n = 10000 -- depth + + local function foo () + if n == 0 then return 1023 + else n = n - 1; return foo() + end + end + + -- build a chain of __call metamethods ending in function 'foo' + for i = 1, 100 do + foo = setmetatable({}, {__call = foo}) + end + + -- call the first one as a tail call in a new coroutine + -- (to ensure stack is not preallocated) + assert(coroutine.wrap(function() return foo() end)() == 1023) +end + print('+')