From b07fc10e91a5954254b47280aba287220c734a4b Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 13 Jan 2021 13:54:10 -0300 Subject: Allow yields inside '__close' metamethods Initial implementation to allow yields inside '__close' metamethods. This current version still does not allow a '__close' metamethod to yield when called due to an error. '__close' metamethods from C functions also are not allowed to yield. --- testes/locals.lua | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'testes') diff --git a/testes/locals.lua b/testes/locals.lua index add023ca..c9c93ccf 100644 --- a/testes/locals.lua +++ b/testes/locals.lua @@ -640,6 +640,94 @@ end print "to-be-closed variables in coroutines" +do + -- yielding inside closing metamethods + + local function checktable (t1, t2) + assert(#t1 == #t2) + for i = 1, #t1 do + assert(t1[i] == t2[i]) + end + end + + local trace = {} + local co = coroutine.wrap(function () + + trace[#trace + 1] = "nowX" + + -- will be closed after 'y' + local x = func2close(function (_, msg) + assert(msg == nil) + trace[#trace + 1] = "x1" + coroutine.yield("x") + trace[#trace + 1] = "x2" + end) + + return pcall(function () + do -- 'z' will be closed first + local z = func2close(function (_, msg) + assert(msg == nil) + trace[#trace + 1] = "z1" + coroutine.yield("z") + trace[#trace + 1] = "z2" + end) + end + + trace[#trace + 1] = "nowY" + + -- will be closed after 'z' + local y = func2close(function(_, msg) + assert(msg == nil) + trace[#trace + 1] = "y1" + coroutine.yield("y") + trace[#trace + 1] = "y2" + end) + + return 10, 20, 30 + end) + end) + + assert(co() == "z") + assert(co() == "y") + assert(co() == "x") + checktable({co()}, {true, 10, 20, 30}) + checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"}) + +end + + +do + -- yielding inside closing metamethods after an error: + -- not yet implemented; raises an error + + local co = coroutine.wrap(function () + + local function foo (err) + + local x = func2close(function(_, msg) + assert(msg == err) + coroutine.yield("x") + return 100, 200 + end) + + if err then error(err) else return 10, 20 end + end + + coroutine.yield(pcall(foo, nil)) -- no error + return pcall(foo, 10) -- 'foo' will raise an error + end) + + local a, b = co() + assert(a == "x" and b == nil) -- yields inside 'x'; Ok + + local a, b, c = co() + assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)' + + local st, msg = co() -- error yielding after an error + assert(not st and string.find(msg, "attempt to yield")) +end + + do -- an error in a wrapped coroutine closes variables local x = false -- cgit v1.2.3-55-g6feb