Error object cannot be nil

Lua will change a nil as error object to a string message, so that it
never reports an error with nil as the error object.
This commit is contained in:
Roberto Ierusalimschy
2025-02-28 14:53:58 -03:00
parent 127a8e80fe
commit ee99452158
4 changed files with 23 additions and 8 deletions

View File

@@ -844,7 +844,9 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
va_start(argp, fmt); va_start(argp, fmt);
msg = luaO_pushvfstring(L, fmt, argp); /* format message */ msg = luaO_pushvfstring(L, fmt, argp); /* format message */
va_end(argp); va_end(argp);
if (msg != NULL && isLua(ci)) { /* Lua function? (and no error) */ if (msg == NULL) /* no memory to format message? */
luaD_throw(L, LUA_ERRMEM);
else if (isLua(ci)) { /* Lua function? */
/* add source:line information */ /* add source:line information */
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */

10
ldo.c
View File

@@ -112,12 +112,16 @@ void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
break; break;
} }
default: { default: {
lua_assert(errorstatus(errcode)); /* real error */ lua_assert(errorstatus(errcode)); /* must be a real error */
setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ if (!ttisnil(s2v(L->top.p - 1))) { /* error object is not nil? */
setobjs2s(L, oldtop, L->top.p - 1); /* move it to 'oldtop' */
}
else /* change it to a proper message */
setsvalue2s(L, oldtop, luaS_newliteral(L, "<error object is nil>"));
break; break;
} }
} }
L->top.p = oldtop + 1; L->top.p = oldtop + 1; /* top goes back to old top plus error object */
} }

View File

@@ -290,7 +290,9 @@ an @def{error object}
is propagated with information about the error. is propagated with information about the error.
Lua itself only generates errors whose error object is a string, Lua itself only generates errors whose error object is a string,
but programs can generate errors with but programs can generate errors with
any value as the error object. any value as the error object,
except @nil.
(Lua will change a @nil as error object to a string message.)
It is up to the Lua program or its host to handle such error objects. It is up to the Lua program or its host to handle such error objects.
For historical reasons, For historical reasons,
an error object is often called an @def{error message}, an error object is often called an @def{error message},
@@ -8082,6 +8084,8 @@ multiple assignment:
The default for @id{a2} is @id{a1}. The default for @id{a2} is @id{a1}.
The destination range can overlap with the source range. The destination range can overlap with the source range.
The number of elements to be moved must fit in a Lua integer. The number of elements to be moved must fit in a Lua integer.
If @id{f} is larger than @id{e},
nothing is moved.
Returns the destination table @id{a2}. Returns the destination table @id{a2}.
@@ -9402,6 +9406,11 @@ declare a local variable with the same name in the loop body.
A chain of @id{__call} metamethods can have at most 15 objects. A chain of @id{__call} metamethods can have at most 15 objects.
} }
@item{
In an error, a @nil as the error object is replaced by a
string message.
}
} }
} }

View File

@@ -46,7 +46,7 @@ end
assert(doit("error('hi', 0)") == 'hi') assert(doit("error('hi', 0)") == 'hi')
-- test nil error message -- test nil error message
assert(doit("error()") == nil) assert(doit("error()") == "<error object is nil>")
-- test common errors/errors that crashed in the past -- test common errors/errors that crashed in the past
@@ -614,7 +614,7 @@ do
assert(not res and msg == t) assert(not res and msg == t)
res, msg = pcall(function () error(nil) end) res, msg = pcall(function () error(nil) end)
assert(not res and msg == nil) assert(not res and msg == "<error object is nil>")
local function f() error{msg='x'} end local function f() error{msg='x'} end
res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end) res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end)
@@ -634,7 +634,7 @@ do
assert(not res and msg == t) assert(not res and msg == t)
res, msg = pcall(assert, nil, nil) res, msg = pcall(assert, nil, nil)
assert(not res and msg == nil) assert(not res and type(msg) == "string")
-- 'assert' without arguments -- 'assert' without arguments
res, msg = pcall(assert) res, msg = pcall(assert)