This patch improves Lua's support for custom error objects.  Changes:

    * Uncaught error handler in standard Lua interpreter calls tostring()
      on error object.  This ensures that a call stack is displayed even
      for non-string error objects.  It also allows use of the __tostring
      hook for human-readable error messages.

    * Base library error() will set the _WHERE field of any table error
      object to the value of luaL_where().  Uncaught error handler in the
      standard Lua interpreter will use this, so that for custom error
      object the error location is shown in the call stack.  This is a bit
      of a hack and implies that any thrown table should be a unique
      instance of the error (since it will be mutated).  Rather than this
      scheme, the idea solution would be to have the Lua core manage the
      location separate from the error object.

See "Exception Patterns in Lua"
(http://memebeam.org/john/lua/exception_patterns.odp) for more information.


diff -urN lua-5.1.1/src/lbaselib.c lua-5.1.1_custom_errors/src/lbaselib.c
--- lua-5.1.1/src/lbaselib.c	2006-06-02 11:34:00.000000000 -0400
+++ lua-5.1.1_custom_errors/src/lbaselib.c	2006-09-06 10:29:52.000000000 -0400
@@ -81,10 +81,14 @@
 static int luaB_error (lua_State *L) {
   int level = luaL_optint(L, 2, 1);
   lua_settop(L, 1);
-  if (lua_isstring(L, 1) && level > 0) {  /* add extra information? */
+  if ((lua_isstring(L, 1) || lua_istable(L, 1)) && level > 0) {  /* add extra information? */
     luaL_where(L, level);
-    lua_pushvalue(L, 1);
-    lua_concat(L, 2);
+    if (lua_isstring(L, 1)) {
+      lua_pushvalue(L, 1);
+      lua_concat(L, 2);
+    }
+    else
+      lua_setfield(L, 1, "_WHERE");
   }
   return lua_error(L);
 }
diff -urN lua-5.1.1/src/lua.c lua-5.1.1_custom_errors/src/lua.c
--- lua-5.1.1/src/lua.c	2006-06-02 11:34:00.000000000 -0400
+++ lua-5.1.1_custom_errors/src/lua.c	2006-09-06 11:26:15.000000000 -0400
@@ -84,7 +84,24 @@
     lua_pop(L, 2);
     return 1;
   }
-  lua_pushvalue(L, 1);  /* pass error message */
+  lua_getglobal(L, "tostring");
+  if (!lua_isfunction(L, -1)) {
+    lua_pop(L, 1);
+    lua_pushvalue(L, 1);  /* pass error object */
+  }
+  else {
+    lua_pushvalue(L, 1);
+    lua_call(L, 1, 1);  /* call tostring on error object */
+  }
+  if (lua_istable(L, 1)) {  /* use _WHERE on table error if it exists */
+    lua_getfield(L, 1, "_WHERE");
+    if (lua_isstring(L, -1)) {
+      lua_insert(L, -2);
+      lua_concat(L, 2);
+    }
+    else
+      lua_pop(L, 1);
+  }
   lua_pushinteger(L, 2);  /* skip this function and traceback */
   lua_call(L, 2, 1);  /* call debug.traceback */
   return 1;
