lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On 9-Feb-07, at 12:46 PM, Doug Rogers wrote:

Nodir Temirhodzhaev wrote:

This finally block must be executed once whenever returned from function
or error throwed.
funcbody ::= '(' [parlist1] ')' block1 [finally block2] end

The use case (cleaning up after errors) is like exceptions in Ada, but
what you propose is more general, but without passing arguments to the
handler. I think it would be even more general, and would provide the
ability to clean up inner scoped locals, if 'finally' were applied to
code blocks. So:

  block ::= chunk | finally { stat[;] }

It can't replace chunk since chunk includes a return or break statement.

I believe this would preclude tail calls in functions with finalization.

Not necessarily. I sketched out an implementation of finally clauses
in the midst of a longer discussion about non-local exits some
time ago. Thanks to the lua-l archives time machine, here it is:
<http://lua-users.org/lists/lua-l/2005-08/msg00357.html>

The question is, how to make this work with tailcalls. In OP_RETURN
and OP_TAILCALL, open upvalues are closed down to the beginning
of the call-frame. With the implementation sketched out in the
preceding message, that would trigger the finalizer, but
unfortunately it will finalize too soon in the case of a tailcall
(i.e. it will trigger before the tailcall.)

However, there is a stack slot associated with the current
function, effectively stack slot 0. If we associate the finalizer
with slot 0 instead of slot 1, and only close upvalues down to
slot 1 on a tailcall, the finalizer will not be triggered until
the call-frame is popped, which is the correct moment.

Of course, it may be the case that more than one function installs
a finalizer on the same call-frame, but that will work fine as long
as the order of upvalues on the open upvalue chain is kept stable.
Currently, it's impossible to have more than one upvalue on the
chain with the same level, but I don't see any reason why this
invariant can't be loosened a bit.

Since the call-frame itself is cleared by a tailcall, the finalizer
cannot refer to any locals in the function for which it is a finalizer
without turning those locals into upvalues. However, compiling
finalizers into functions. That fails to take advantage of
the potential efficiencies of not turning locals into upvalues.
An optimization of that form would require a little more work, and
it would, in my opinion, be necessary to demonstrate that it was
actually necessary before attempting an implementation.