This is what the manual says:
Internally, Lua uses the C longjmp facility to handle errors. (Lua
will use exceptions if you compile it as C++; search for LUAI_THROW in
the source code for details.)
It says even less in another place:
Internally, Lua uses the C longjmp facility to yield a coroutine.
While in fact exactly the same mechanism is used in both cases (at
least in recent versions), with the same implication when compiling as
C++.
1. If Lua is compiled as plain C and used with a C++ library or host,
raising a Lua error (or yielding, also in what follows) might result
in undefined behavior (if it unwinds any stack frame with a C++ object
that has a non-trivial destructor). It is tempting to insist that the
manual ought to say "whenever Lua is likely to be used with C++
libraries or hosts, it should be compiled as C++ as a rule", but we
have #3 below.
2. If Lua is compiled as C++ and used with (solely) C++ libs and
hosts, then, generally speaking, the code that calls any error-raising
Lua API must be exception safe (in the C++ sense). While that might
seem obvious, it is also worth noting in the manual. Even though
writing exception-safe code in C++ is not too difficult, this does
have a cost, both in terms of dev effort and runtime performance.
3. If Lua is compiled as C++ and used with plain C libs or hosts,
calling error-raising Lua API is at best implementation-defined
behavior when it involves propagating a C++ exception through (and
out) plain C code. If the libs or host or Lua are compiled
independently, this is a problem without a solution (except by a lucky
chance). This is especially nasty when both C and C++ libs need to be
used.
4. If Lua is compiled as plain C and used with (solely) plain C libs
and hosts, the code that calls any error-raising Lua API must be
"exception safe", except that the language provides no means for that.
I suppose the Lua stack could be used for that purpose, but even then
it is not too easy. Consider this:
char *p = malloc(length);
/* construct some string at p */
lua_pushstring(L, p);
free(p);
This code is unsafe, because lua_pushstring(p) may raise an error (out
of memory), which leaks the allocated buffer. One way to solve this
is:
char *p = lua_newuserdatauv(L, length, 0);
/* construct some string at p */
lua_pushstring(L, p);
lua_remove(L, -2);
I won't say anything about the elegance of that, but we now have a
complication, if we originally wanted to save p and use it internally.
We might use malloc() again to create a clone, but things get more
complicated when one needs to allocate userdata with nested pointers
to some other memory allocations.
Probably a more robust alternative to the above, which could deal with
those complications, is this:
/* m is a metatable with __gc that calls free(), all omitted for brevity */
char *p, **q = lua_newuserdatauv(L, sizeof p, 0);
lua_geti(L, LUA_REGISTRYINDEX, m);
lua_setmetatable(L, -2);
*q = p = malloc(length);
/* construct some string at p */
Now we can do *q = 0 and detach the lifetime of the allocated memory
from the user datum, or we could keep it and, in case we have some
master user datum that references this memory, make it its user value.
But note the amount of code required for all that, and that is after
half the code has been omitted.
I'd think the manual should say something about these complications
and offer a way to deal with them.
Then we might have a C/C++ library or host calling longjmp() or
throwing an exception at Lua. The manual is completely silent about
that. In my previous message I suggested that this should never be
done, which is the safest option, but having some clarity about that
would not hurt. In principle, the same four cases might be considered
in this regard. I’d think the implementers of Lua should be in a
better position to do this.
Cheers,
V.