Browse Source

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.
Roberto Ierusalimschy 5 months ago
parent
commit
ee99452158
4 changed files with 23 additions and 8 deletions
  1. 3 1
      ldebug.c
  2. 7 3
      ldo.c
  3. 10 1
      manual/manual.of
  4. 3 3
      testes/errors.lua

+ 3 - 1
ldebug.c

@@ -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' */

+ 7 - 3
ldo.c

@@ -112,12 +112,16 @@ void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
       break;
       break;
     }
     }
     default: {
     default: {
-      lua_assert(errorstatus(errcode));  /* real error */
-      setobjs2s(L, oldtop, L->top.p - 1);  /* error message on current top */
+      lua_assert(errorstatus(errcode));  /* must be a real error */
+      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 */
 }
 }
 
 
 
 

+ 10 - 1
manual/manual.of

@@ -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.
+}
+
 }
 }
 
 
 }
 }

+ 3 - 3
testes/errors.lua

@@ -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)