diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-06-14 13:28:21 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-06-14 13:28:21 -0300 |
commit | 04e19712a5d48b84869f9942836ff8314fb0be8e (patch) | |
tree | 75aab8dff5bdf07026080eba0189e4025ec3b7ee | |
parent | 901d76009346d76996679c02deee708bf225e91e (diff) | |
download | lua-04e19712a5d48b84869f9942836ff8314fb0be8e.tar.gz lua-04e19712a5d48b84869f9942836ff8314fb0be8e.tar.bz2 lua-04e19712a5d48b84869f9942836ff8314fb0be8e.zip |
C functions can be tail called, too
A tail call to a C function can have the behavior of a "real" tail
call, reusing the stack frame of the caller.
-rw-r--r-- | ldo.c | 43 | ||||
-rw-r--r-- | lvm.c | 9 | ||||
-rw-r--r-- | testes/coroutine.lua | 2 | ||||
-rw-r--r-- | testes/errors.lua | 4 |
4 files changed, 29 insertions, 29 deletions
@@ -478,12 +478,31 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
478 | ** (This is done only when no more errors can occur before entering the | 478 | ** (This is done only when no more errors can occur before entering the |
479 | ** new function, to keep debug information always consistent.) | 479 | ** new function, to keep debug information always consistent.) |
480 | */ | 480 | */ |
481 | static void moveparams (lua_State *L, StkId prevf, StkId func, int narg) { | 481 | static void moveparams (lua_State *L, StkId prevf, StkId func) { |
482 | int i; | 482 | int i; |
483 | narg++; /* function itself will be moved, too */ | 483 | for (i = 0; func + i < L->top; i++) /* move down function and arguments */ |
484 | for (i = 0; i < narg; i++) /* move down function and arguments */ | ||
485 | setobjs2s(L, prevf + i, func + i); | 484 | setobjs2s(L, prevf + i, func + i); |
486 | L->top = prevf + narg; /* correct top */ | 485 | L->top = prevf + i; /* correct top */ |
486 | } | ||
487 | |||
488 | |||
489 | static CallInfo *prepCallInfo (lua_State *L, StkId func, int nresults, | ||
490 | int delta1, int mask) { | ||
491 | CallInfo *ci; | ||
492 | if (delta1) { /* tail call? */ | ||
493 | ci = L->ci; /* reuse stack frame */ | ||
494 | ci->func -= delta1 - 1; /* correct 'func' */ | ||
495 | |||
496 | ci->callstatus |= mask | CIST_TAIL; | ||
497 | moveparams(L, ci->func, func); | ||
498 | } | ||
499 | else { /* regular call */ | ||
500 | ci = L->ci = next_ci(L); /* new frame */ | ||
501 | ci->func = func; | ||
502 | ci->nresults = nresults; | ||
503 | ci->callstatus = mask; | ||
504 | } | ||
505 | return ci; | ||
487 | } | 506 | } |
488 | 507 | ||
489 | 508 | ||
@@ -512,11 +531,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) { | |||
512 | int n; /* number of returns */ | 531 | int n; /* number of returns */ |
513 | CallInfo *ci; | 532 | CallInfo *ci; |
514 | checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ | 533 | checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ |
515 | L->ci = ci = next_ci(L); | 534 | ci = prepCallInfo(L, func, nresults, delta1, CIST_C); |
516 | ci->nresults = nresults; | ||
517 | ci->callstatus = CIST_C; | ||
518 | ci->top = L->top + LUA_MINSTACK; | 535 | ci->top = L->top + LUA_MINSTACK; |
519 | ci->func = func; | ||
520 | lua_assert(ci->top <= L->stack_last); | 536 | lua_assert(ci->top <= L->stack_last); |
521 | if (l_unlikely(L->hookmask & LUA_MASKCALL)) { | 537 | if (l_unlikely(L->hookmask & LUA_MASKCALL)) { |
522 | int narg = cast_int(L->top - func) - 1; | 538 | int narg = cast_int(L->top - func) - 1; |
@@ -536,16 +552,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) { | |||
536 | int nfixparams = p->numparams; | 552 | int nfixparams = p->numparams; |
537 | int fsize = p->maxstacksize; /* frame size */ | 553 | int fsize = p->maxstacksize; /* frame size */ |
538 | checkstackGCp(L, fsize, func); | 554 | checkstackGCp(L, fsize, func); |
539 | if (delta1) { /* tail call? */ | 555 | ci = prepCallInfo(L, func, nresults, delta1, 0); |
540 | ci = L->ci; /* reuse stack frame */ | ||
541 | ci->func -= delta1 - 1; /* correct 'func' */ | ||
542 | moveparams(L, ci->func, func, narg); | ||
543 | } | ||
544 | else { /* regular call */ | ||
545 | L->ci = ci = next_ci(L); /* new frame */ | ||
546 | ci->func = func; | ||
547 | ci->nresults = nresults; | ||
548 | } | ||
549 | ci->u.l.savedpc = p->code; /* starting point */ | 556 | ci->u.l.savedpc = p->code; /* starting point */ |
550 | ci->top = func + 1 + fsize; | 557 | ci->top = func + 1 + fsize; |
551 | for (; narg < nfixparams; narg++) | 558 | for (; narg < nfixparams; narg++) |
@@ -1636,7 +1636,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1636 | updatetrap(ci); /* C call; nothing else to be done */ | 1636 | updatetrap(ci); /* C call; nothing else to be done */ |
1637 | else { /* Lua call: run function in this same C frame */ | 1637 | else { /* Lua call: run function in this same C frame */ |
1638 | ci = newci; | 1638 | ci = newci; |
1639 | ci->callstatus = 0; | ||
1640 | goto startfunc; | 1639 | goto startfunc; |
1641 | } | 1640 | } |
1642 | vmbreak; | 1641 | vmbreak; |
@@ -1655,16 +1654,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1655 | lua_assert(L->tbclist < base); /* no pending tbc variables */ | 1654 | lua_assert(L->tbclist < base); /* no pending tbc variables */ |
1656 | lua_assert(base == ci->func + 1); | 1655 | lua_assert(base == ci->func + 1); |
1657 | } | 1656 | } |
1658 | if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) { /* Lua function? */ | 1657 | if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) /* Lua function? */ |
1659 | ci->callstatus |= CIST_TAIL; | ||
1660 | goto startfunc; /* execute the callee */ | 1658 | goto startfunc; /* execute the callee */ |
1661 | } | ||
1662 | else { /* C function */ | 1659 | else { /* C function */ |
1663 | updatetrap(ci); | 1660 | updatetrap(ci); |
1664 | updatestack(ci); /* stack may have been relocated */ | ||
1665 | ci->func -= delta; /* restore 'func' (if vararg) */ | ||
1666 | luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */ | ||
1667 | updatetrap(ci); /* 'luaD_poscall' can change hooks */ | ||
1668 | goto ret; /* caller returns after the tail call */ | 1661 | goto ret; /* caller returns after the tail call */ |
1669 | } | 1662 | } |
1670 | } | 1663 | } |
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index b36b76ea..461e0377 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
@@ -205,7 +205,7 @@ do | |||
205 | co = coroutine.create(function () return pcall(foo) end) | 205 | co = coroutine.create(function () return pcall(foo) end) |
206 | local st1, st2, err = coroutine.resume(co) | 206 | local st1, st2, err = coroutine.resume(co) |
207 | assert(st1 and not st2 and err == 43) | 207 | assert(st1 and not st2 and err == 43) |
208 | assert(X == 43 and Y.name == "pcall") | 208 | assert(X == 43 and Y.what == "C") |
209 | 209 | ||
210 | -- recovering from errors in __close metamethods | 210 | -- recovering from errors in __close metamethods |
211 | local track = {} | 211 | local track = {} |
diff --git a/testes/errors.lua b/testes/errors.lua index a3d0676b..825f37c2 100644 --- a/testes/errors.lua +++ b/testes/errors.lua | |||
@@ -26,7 +26,7 @@ end | |||
26 | 26 | ||
27 | local function checkmessage (prog, msg, debug) | 27 | local function checkmessage (prog, msg, debug) |
28 | local m = doit(prog) | 28 | local m = doit(prog) |
29 | if debug then print(m) end | 29 | if debug then print(m, msg) end |
30 | assert(string.find(m, msg, 1, true)) | 30 | assert(string.find(m, msg, 1, true)) |
31 | end | 31 | end |
32 | 32 | ||
@@ -289,7 +289,7 @@ end]], "global 'insert'") | |||
289 | 289 | ||
290 | checkmessage([[ -- tail call | 290 | checkmessage([[ -- tail call |
291 | return math.sin("a") | 291 | return math.sin("a") |
292 | ]], "'sin'") | 292 | ]], "sin") |
293 | 293 | ||
294 | checkmessage([[collectgarbage("nooption")]], "invalid option") | 294 | checkmessage([[collectgarbage("nooption")]], "invalid option") |
295 | 295 | ||