aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2025-01-28 11:45:45 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2025-01-28 11:45:45 -0300
commit39a14ea7d7b14172595c61619e8f35c2614b2606 (patch)
tree863ea56e6014de9e1284f2e47ac1fbd4b69339ed
parentc4e7cdb541d89142056927ebdfd8f97017d38f45 (diff)
downloadlua-39a14ea7d7b14172595c61619e8f35c2614b2606.tar.gz
lua-39a14ea7d7b14172595c61619e8f35c2614b2606.tar.bz2
lua-39a14ea7d7b14172595c61619e8f35c2614b2606.zip
CallInfo bit CIST_CLSRET broken in two
Since commit f407b3c4a, it was being used for two distinct (and incompatible) meanings: A: Function has TBC variables (now bit CIST_TBC) B: Interpreter is closing TBC variables (original bit CIST_CLSRET) B implies A, but A does not imply B.
-rw-r--r--lapi.c6
-rw-r--r--ldo.c12
-rw-r--r--lstate.h4
-rw-r--r--testes/coroutine.lua37
4 files changed, 44 insertions, 15 deletions
diff --git a/lapi.c b/lapi.c
index c0fd1a1b..7b30617f 100644
--- a/lapi.c
+++ b/lapi.c
@@ -195,7 +195,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
195 } 195 }
196 newtop = L->top.p + diff; 196 newtop = L->top.p + diff;
197 if (diff < 0 && L->tbclist.p >= newtop) { 197 if (diff < 0 && L->tbclist.p >= newtop) {
198 lua_assert(ci->callstatus & CIST_CLSRET); 198 lua_assert(ci->callstatus & CIST_TBC);
199 newtop = luaF_close(L, newtop, CLOSEKTOP, 0); 199 newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
200 } 200 }
201 L->top.p = newtop; /* correct top only after closing any upvalue */ 201 L->top.p = newtop; /* correct top only after closing any upvalue */
@@ -207,7 +207,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
207 StkId level; 207 StkId level;
208 lua_lock(L); 208 lua_lock(L);
209 level = index2stack(L, idx); 209 level = index2stack(L, idx);
210 api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level, 210 api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level),
211 "no variable to close at given level"); 211 "no variable to close at given level");
212 level = luaF_close(L, level, CLOSEKTOP, 0); 212 level = luaF_close(L, level, CLOSEKTOP, 0);
213 setnilvalue(s2v(level)); 213 setnilvalue(s2v(level));
@@ -1280,7 +1280,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
1280 o = index2stack(L, idx); 1280 o = index2stack(L, idx);
1281 api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); 1281 api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
1282 luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ 1282 luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
1283 L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */ 1283 L->ci->callstatus |= CIST_TBC; /* mark that function has TBC slots */
1284 lua_unlock(L); 1284 lua_unlock(L);
1285} 1285}
1286 1286
diff --git a/ldo.c b/ldo.c
index 994ad6f0..31c00a21 100644
--- a/ldo.c
+++ b/ldo.c
@@ -505,7 +505,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres,
505** Given 'nres' results at 'firstResult', move 'fwanted-1' of them 505** Given 'nres' results at 'firstResult', move 'fwanted-1' of them
506** to 'res'. Handle most typical cases (zero results for commands, 506** to 'res'. Handle most typical cases (zero results for commands,
507** one result for expressions, multiple results for tail calls/single 507** one result for expressions, multiple results for tail calls/single
508** parameters) separated. The flag CIST_CLSRET in 'fwanted', if set, 508** parameters) separated. The flag CIST_TBC in 'fwanted', if set,
509** forces the swicth to go to the default case. 509** forces the swicth to go to the default case.
510*/ 510*/
511l_sinline void moveresults (lua_State *L, StkId res, int nres, 511l_sinline void moveresults (lua_State *L, StkId res, int nres,
@@ -526,8 +526,9 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
526 break; 526 break;
527 default: { /* two/more results and/or to-be-closed variables */ 527 default: { /* two/more results and/or to-be-closed variables */
528 int wanted = get_nresults(fwanted); 528 int wanted = get_nresults(fwanted);
529 if (fwanted & CIST_CLSRET) { /* to-be-closed variables? */ 529 if (fwanted & CIST_TBC) { /* to-be-closed variables? */
530 L->ci->u2.nres = nres; 530 L->ci->u2.nres = nres;
531 L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
531 res = luaF_close(L, res, CLOSEKTOP, 1); 532 res = luaF_close(L, res, CLOSEKTOP, 1);
532 L->ci->callstatus &= ~CIST_CLSRET; 533 L->ci->callstatus &= ~CIST_CLSRET;
533 if (L->hookmask) { /* if needed, call hook after '__close's */ 534 if (L->hookmask) { /* if needed, call hook after '__close's */
@@ -552,8 +553,8 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
552** that. 553** that.
553*/ 554*/
554void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { 555void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
555 l_uint32 fwanted = ci->callstatus & (CIST_CLSRET | CIST_NRESULTS); 556 l_uint32 fwanted = ci->callstatus & (CIST_TBC | CIST_NRESULTS);
556 if (l_unlikely(L->hookmask) && !(fwanted & CIST_CLSRET)) 557 if (l_unlikely(L->hookmask) && !(fwanted & CIST_TBC))
557 rethook(L, ci, nres); 558 rethook(L, ci, nres);
558 /* move results to proper place */ 559 /* move results to proper place */
559 moveresults(L, ci->func.p, nres, fwanted); 560 moveresults(L, ci->func.p, nres, fwanted);
@@ -785,7 +786,8 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
785*/ 786*/
786static void finishCcall (lua_State *L, CallInfo *ci) { 787static void finishCcall (lua_State *L, CallInfo *ci) {
787 int n; /* actual number of results from C function */ 788 int n; /* actual number of results from C function */
788 if (ci->callstatus & CIST_CLSRET) { /* was returning? */ 789 if (ci->callstatus & CIST_CLSRET) { /* was closing TBC variable? */
790 lua_assert(ci->callstatus & CIST_TBC);
789 n = ci->u2.nres; /* just redo 'luaD_poscall' */ 791 n = ci->u2.nres; /* just redo 'luaD_poscall' */
790 /* don't need to reset CIST_CLSRET, as it will be set again anyway */ 792 /* don't need to reset CIST_CLSRET, as it will be set again anyway */
791 } 793 }
diff --git a/lstate.h b/lstate.h
index e95c7288..635f41d2 100644
--- a/lstate.h
+++ b/lstate.h
@@ -235,8 +235,10 @@ struct CallInfo {
235#define CIST_FRESH cast(l_uint32, CIST_C << 1) 235#define CIST_FRESH cast(l_uint32, CIST_C << 1)
236/* function is closing tbc variables */ 236/* function is closing tbc variables */
237#define CIST_CLSRET (CIST_FRESH << 1) 237#define CIST_CLSRET (CIST_FRESH << 1)
238/* function has tbc variables to close */
239#define CIST_TBC (CIST_CLSRET << 1)
238/* original value of 'allowhook' */ 240/* original value of 'allowhook' */
239#define CIST_OAH (CIST_CLSRET << 1) 241#define CIST_OAH (CIST_TBC << 1)
240/* call is running a debug hook */ 242/* call is running a debug hook */
241#define CIST_HOOKED (CIST_OAH << 1) 243#define CIST_HOOKED (CIST_OAH << 1)
242/* doing a yieldable protected call */ 244/* doing a yieldable protected call */
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index c1252ab8..78b9bdca 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -515,7 +515,7 @@ else
515 print "testing yields inside hooks" 515 print "testing yields inside hooks"
516 516
517 local turn 517 local turn
518 518
519 local function fact (t, x) 519 local function fact (t, x)
520 assert(turn == t) 520 assert(turn == t)
521 if x == 0 then return 1 521 if x == 0 then return 1
@@ -642,7 +642,7 @@ else
642 642
643 643
644 print "testing coroutine API" 644 print "testing coroutine API"
645 645
646 -- reusing a thread 646 -- reusing a thread
647 assert(T.testC([[ 647 assert(T.testC([[
648 newthread # create thread 648 newthread # create thread
@@ -920,7 +920,7 @@ do -- a few more tests for comparison operators
920 until res ~= 10 920 until res ~= 10
921 return res 921 return res
922 end 922 end
923 923
924 local function test () 924 local function test ()
925 local a1 = setmetatable({x=1}, mt1) 925 local a1 = setmetatable({x=1}, mt1)
926 local a2 = setmetatable({x=2}, mt2) 926 local a2 = setmetatable({x=2}, mt2)
@@ -932,7 +932,7 @@ do -- a few more tests for comparison operators
932 assert(2 >= a2) 932 assert(2 >= a2)
933 return true 933 return true
934 end 934 end
935 935
936 run(test) 936 run(test)
937 937
938end 938end
@@ -1037,6 +1037,31 @@ f = T.makeCfunc([[
1037 return * 1037 return *
1038]], 23, "huu") 1038]], 23, "huu")
1039 1039
1040
1041do -- testing bug introduced in commit f407b3c4a
1042 local X = false -- flag "to be closed"
1043 local coro = coroutine.wrap(T.testC)
1044 -- runs it until 'pcallk' (that yields)
1045 -- 4th argument (at index 4): object to be closed
1046 local res1, res2 = coro(
1047 [[
1048 toclose 3 # this could break the next 'pcallk'
1049 pushvalue 2 # push function 'yield' to call it
1050 pushint 22; pushint 33 # arguments to yield
1051 # calls 'yield' (2 args; 2 results; continuation function at index 4)
1052 pcallk 2 2 4
1053 invalid command (should not arrive here)
1054 ]], -- 1st argument (at index 1): code;
1055 coroutine.yield, -- (at index 2): function to be called
1056 func2close(function () X = true end), -- (index 3): TBC slot
1057 "pushint 43; return 3" -- (index 4): code for continuation function
1058 )
1059
1060 assert(res1 == 22 and res2 == 33 and not X)
1061 local res1, res2, res3 = coro(34, "hi") -- runs continuation function
1062 assert(res1 == 34 and res2 == "hi" and res3 == 43 and X)
1063end
1064
1040x = coroutine.wrap(f) 1065x = coroutine.wrap(f)
1041assert(x() == 102) 1066assert(x() == 102)
1042eqtab({x()}, {23, "huu"}) 1067eqtab({x()}, {23, "huu"})
@@ -1094,11 +1119,11 @@ co = coroutine.wrap(function (...) return
1094 cannot be here! 1119 cannot be here!
1095 ]], 1120 ]],
1096 [[ # 1st continuation 1121 [[ # 1st continuation
1097 yieldk 0 3 1122 yieldk 0 3
1098 cannot be here! 1123 cannot be here!
1099 ]], 1124 ]],
1100 [[ # 2nd continuation 1125 [[ # 2nd continuation
1101 yieldk 0 4 1126 yieldk 0 4
1102 cannot be here! 1127 cannot be here!
1103 ]], 1128 ]],
1104 [[ # 3th continuation 1129 [[ # 3th continuation