goto jumps into scope of local (Lua 5.2.0-beta)

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

goto jumps into scope of local (Lua 5.2.0-beta)

David Manura
In Lua 5.2.0-beta-rc4, gotos allow transactional error handling code
to be written something like this:

  -- 5.2.0-beta-rc2
  function perform_transaction()
    if not f() then goto undone_f end  -- [*2]
    if not g() then goto undone_g end  -- [*2]
    local hval = h()
    if not hval then goto undone_h end  -- [*2]

    local x
    .....

    return true  -- success [*1]

    -- rollback
    ::undone_h::
    undo_g()  ::undone_g::
    undo_f()  ::undone_f::
    print(x)

    return false  -- failure
  end

except for two issues.  First, the "return true" is required to be
wrapped in do/end [*1].  Secondly, and more importantly, the gotos
will be rejected as written because they jump into the scope of locals
[*2] ....or do they?  The reference manual narrows variable scope
slightly:

  "The scope of a local variable begins at the first statement after
its declaration and lasts until the last non-void statement of the
innermost block that includes the declaration."

thereby permitting things like continue: `whlie ..... goto continue
..... local x ..... ::continue:: end`.  It would be possible though to
tighten the scope rules further to allow the error handling code
above.  The new rule would be that the scope of a local ends at a
label that is jumped to from outside that variable's scope.
Therefore, the `print(x)` above will print nil.

However, the Lua 5.1 way of doing all this may still be easier to understand:

  function perform_transaction()
    local fval, gval, hval
    repeat
      fval = f(); if not fval then break end
      gval = g(); if not gval then break end
      hval = h(); if not hval then break end  -- alternately `goto`
the precise rollback statement below
      local x
      .....
      return true
    until false  -- an abuse of repeat; alternately use `do ..... goto
last ..... end ::last::`

    -- rollback
    if gval then undo_g() end -- alternately add labels here to jump to
    if fval then undo_f() end

    return false  -- failure
  end

There are some redundant conditionals in the rollback code, which can
now be eliminated with gotos again, but as with the case with
exception handling, we can normally assume that traversal of the error
handling code path is rare and not performance critical.

Now, more ideally, a macro preprocessor could transform the below code
into the above:

  function perform_transaction()
    ROLLBACK.FAIL return false
    CHECK.FAIL f(); ROLLBACK.FAIL undo_f()
    CHECK.FAIL g(); ROLLBACK.FAIL undo_g()
    hval = h(); CHECK.FAIL hval
    local x
    .....
    return true
  end

The idea is that the ROLLBACK prefixed statements are normally
ignored.  However, if the conditional in a CHECK.X statement is false,
then any ROLLBACK.X statements above it are immediately executed in
reverse order.  To take another example, this:

  function read(filename)
    ROLLBACK.FAIL return nil, err
    local fh, err = io.open(filename); CHECK.FAIL fh; ROLLBACK.ALWAYS fh:close()
    return fh:read()
  end

would be translated into

  function read(filename)
    local fh, err = io.open(filename)

    -- CHECK.FAIL fh
    if not(fh) then goto fail1 end

    -- return fh:read(), part 1 of 2
    local _tmp1 = return fh:read()

    -- ROLLBACK.ALWAYS
    fh:close()

    -- return fh:read(), part 2 of 2
    return _tmp1

    ::fail1::
    -- ROLLBACK.FAIL
    return nil, err
  end

Reply | Threaded
Open this post in threaded view
|

Re: goto jumps into scope of local (Lua 5.2.0-beta)

Miles Bader-2
David Manura <[hidden email]> writes:
> Secondly, and more importantly, the gotos will be rejected as written
> because they jump into the scope of locals [*2] ....

I think people will cope OK...

There are several obvious ways to do so --

(1) in the presence of gotos, pre-declare any variables necessary to
avoid "jumping into a scope":

   local x
   ...
   if err_cond then goto ::cleanup_return::
   ...
   x = something()
   ...
   return x

   ::cleanup_return::
   cleanup()
   return nil

(2) use nested scopes to limit the scope of new variables:

   ...
   if err_cond then goto ::cleanup_return::
   ...
   do
      local x = something()
      ...
      return x
   end

   ::cleanup_return::
   cleanup()
   return nil

-miles

--
Non-combatant, n. A dead Quaker.

Reply | Threaded
Open this post in threaded view
|

Re: goto jumps into scope of local (Lua 5.2.0-beta)

Edgar Toernig
In reply to this post by David Manura
David Manura wrote:

> In Lua 5.2.0-beta-rc4, gotos allow transactional error handling code
> to be written something like this:
>
>   -- 5.2.0-beta-rc2
>   function perform_transaction()
>     if not f() then goto undone_f end  -- [*2]
>     if not g() then goto undone_g end  -- [*2]
>     local hval = h()
>     if not hval then goto undone_h end  -- [*2]
>
>     local x
>     .....
>
>     return true  -- success [*1]
>
>     -- rollback
>     ::undone_h::
>     undo_g()  ::undone_g::
>     undo_f()  ::undone_f::
>     print(x)
>
>     return false  -- failure
>   end
>
> except for two issues.  First, the "return true" is required to be
> wrapped in do/end [*1].  Secondly, and more importantly, the gotos
> will be rejected as written because they jump into the scope of locals

You could catch both issues with a single do/end, the "do" directly
at the start of the function and the "end" after the "return true":

  function foo()
     do
        function body with gotos and locals
        return
     end
     ::labels::
     cleanup
  end

Though I would have no problems if jumping into the scope of new
locals would simply nil them (may even ease error handling).
Doesn't OP_JMP already include the target stack level anyway (for
closing upvalues)?

Ciao, ET.