To-be-closed variables must be closed on initialization

When initializing a to-be-closed variable, check whether it has a
'__close' metamethod (or is a false value) and raise an error if
if it hasn't. This produces more accurate error messages. (The
check before closing still need to be done: in the C API, the value
is not constant; and the object may lose its '__close' metamethod
during the block.)
This commit is contained in:
Roberto Ierusalimschy
2019-07-31 10:43:51 -03:00
parent 35b4efc270
commit f645d31573
6 changed files with 70 additions and 41 deletions

View File

@@ -1096,7 +1096,7 @@ do
assert(type(a[1]) == "string" and a[2][1] == 11)
assert(#openresource == 0) -- was closed
-- error
-- closing by error
local a, b = pcall(T.makeCfunc[[
call 0 1 # create resource
toclose -1 # mark it to be closed
@@ -1105,6 +1105,15 @@ do
assert(a == false and b[1] == 11)
assert(#openresource == 0) -- was closed
-- non-closable value
local a, b = pcall(T.makeCfunc[[
newtable # create non-closable object
toclose -1 # mark it to be closed (shoud raise an error)
abort # will not be executed
]])
assert(a == false and
string.find(b, "non%-closable value"))
local function check (n)
assert(#openresource == n)
end

View File

@@ -215,11 +215,13 @@ end
do
local a = {}
do
local b <close> = false -- not to be closed
local x <close> = setmetatable({"x"}, {__close = function (self)
a[#a + 1] = self[1] end})
local w, y <close>, z = func2close(function (self, err)
assert(err == nil); a[#a + 1] = "y"
end, 10, 20)
local c <close> = nil -- not to be closed
a[#a + 1] = "in"
assert(w == 10 and z == 20)
end
@@ -325,24 +327,22 @@ do -- errors in __close
end
do
-- errors due to non-closable values
do -- errors due to non-closable values
local function foo ()
local x <close> = {}
os.exit(false) -- should not run
end
local stat, msg = pcall(foo)
assert(not stat and string.find(msg, "variable 'x'"))
assert(not stat and
string.find(msg, "variable 'x' got a non%-closable value"))
-- with other errors, non-closable values are ignored
local function foo ()
local x <close> = 34
local y <close> = func2close(function () error(32) end)
local xyz <close> = setmetatable({}, {__close = print})
getmetatable(xyz).__close = nil -- remove metamethod
end
local stat, msg = pcall(foo)
assert(not stat and msg == 32)
assert(not stat and
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
end
@@ -519,7 +519,8 @@ end
-- a suspended coroutine should not close its variables when collected
local co
co = coroutine.wrap(function()
local x <close> = function () os.exit(false) end -- should not run
-- should not run
local x <close> = func2close(function () os.exit(false) end)
co = nil
coroutine.yield()
end)