diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-10-28 15:58:07 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-10-28 15:58:07 -0300 |
commit | 7d526e75a7f45a2593e874d97c7fdfa0e45cc013 (patch) | |
tree | 7c4e612c2d89f2f2addb336154f9b9bb47f2d760 | |
parent | c12983cf8afac4c4c757b84aaddab1935a931641 (diff) | |
download | lua-7d526e75a7f45a2593e874d97c7fdfa0e45cc013.tar.gz lua-7d526e75a7f45a2593e874d97c7fdfa0e45cc013.tar.bz2 lua-7d526e75a7f45a2593e874d97c7fdfa0e45cc013.zip |
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.
-rw-r--r-- | lvm.c | 3 | ||||
-rw-r--r-- | testes/calls.lua | 25 |
2 files changed, 26 insertions, 2 deletions
@@ -1549,9 +1549,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1549 | luaF_close(L, base, NOCLOSINGMETH); | 1549 | luaF_close(L, base, NOCLOSINGMETH); |
1550 | lua_assert(base == ci->func + 1); | 1550 | lua_assert(base == ci->func + 1); |
1551 | } | 1551 | } |
1552 | if (!ttisfunction(s2v(ra))) { /* not a function? */ | 1552 | while (!ttisfunction(s2v(ra))) { /* not a function? */ |
1553 | luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ | 1553 | luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ |
1554 | b++; /* there is now one extra argument */ | 1554 | b++; /* there is now one extra argument */ |
1555 | checkstackp(L, 1, ra); | ||
1555 | } | 1556 | } |
1556 | if (!ttisLclosure(s2v(ra))) { /* C function? */ | 1557 | if (!ttisLclosure(s2v(ra))) { /* C function? */ |
1557 | luaD_call(L, ra, LUA_MULTRET); /* call it */ | 1558 | luaD_call(L, ra, LUA_MULTRET); /* call it */ |
diff --git a/testes/calls.lua b/testes/calls.lua index 739a624f..0141ffa4 100644 --- a/testes/calls.lua +++ b/testes/calls.lua | |||
@@ -107,7 +107,9 @@ end | |||
107 | deep(10) | 107 | deep(10) |
108 | deep(180) | 108 | deep(180) |
109 | 109 | ||
110 | -- testing tail calls | 110 | |
111 | print"testing tail calls" | ||
112 | |||
111 | function deep (n) if n>0 then return deep(n-1) else return 101 end end | 113 | function deep (n) if n>0 then return deep(n-1) else return 101 end end |
112 | assert(deep(30000) == 101) | 114 | assert(deep(30000) == 101) |
113 | a = {} | 115 | a = {} |
@@ -148,6 +150,27 @@ do -- tail calls x varargs | |||
148 | assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30) | 150 | assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30) |
149 | end | 151 | end |
150 | 152 | ||
153 | |||
154 | |||
155 | do -- tail calls x chain of __call | ||
156 | local n = 10000 -- depth | ||
157 | |||
158 | local function foo () | ||
159 | if n == 0 then return 1023 | ||
160 | else n = n - 1; return foo() | ||
161 | end | ||
162 | end | ||
163 | |||
164 | -- build a chain of __call metamethods ending in function 'foo' | ||
165 | for i = 1, 100 do | ||
166 | foo = setmetatable({}, {__call = foo}) | ||
167 | end | ||
168 | |||
169 | -- call the first one as a tail call in a new coroutine | ||
170 | -- (to ensure stack is not preallocated) | ||
171 | assert(coroutine.wrap(function() return foo() end)() == 1023) | ||
172 | end | ||
173 | |||
151 | print('+') | 174 | print('+') |
152 | 175 | ||
153 | 176 | ||