Ensure called twice when raise in ensure
Under very specific circumstances (a
return with code after it), a
raise in an
ensure block causes the
ensure block to be called twice after rescuing in a
rescue block above it.
Here is a very small reproduction case:
class ABC < StandardError; end def meow return puts 'a' rescue ABC puts 'rescue' ensure puts 'ensure' raise ABC end meow
Causes the output
ensure rescue ensure Traceback (most recent call last): 1: from spec/meow.rb:13:in `<main>' spec/meow.rb:10:in `meow': ABC (ABC)
However, if I remove the code below the
return, the behavior is more reasonable:
class ABC < StandardError; end def meow return rescue ABC puts 'rescue' ensure puts 'ensure' raise ABC end meow
ensure Traceback (most recent call last): 1: from spec/meow.rb:14:in `<main>' spec/meow.rb:9:in `meow': ABC (ABC)
It does not seem to matter what code comes after
return. It also does not seem to matter what code comes before the
return, as long as the
return is run. It could throw errors or it might not, but there seems to be a definite requirement that there is code after the
return for this bug to occur.
Possibly related to #13930? I am not sure.
Updated by davidsiaw (David Siaw) over 1 year ago
I found another issue with this where
def meow puts 'start' begin return puts 'should not run' rescue puts 'inner rescue' ensure puts 'inner ensure' end rescue puts 'rescue' ensure puts 'ensure' raise 'heh' end meow
gives the output
start inner ensure ensure inner rescue inner ensure ensure Traceback (most recent call last): 1: from meow.rb:19:in `<main>' meow.rb:16:in `meow': heh (RuntimeError)
It turns out that the raise gets rescued by the inner rescue, not the rescue above it even though that code should have ended already.
Updated by ko1 (Koichi Sasada) 3 months ago
- Status changed from Open to Closed
Applied in changeset git|609de71f043e8ba34f22b9993e444e2e5bb05709.
fix raise in exception with jump
add_ensure_iseq() adds ensure block to the end of
jump such as next/redo/return. However, if the rescue
cause are in the body, this rescue catches the exception
in ensure clause.
In this case, R should not be executed, but executed without this patch.
A part of tests are written by @jeremyevans https://github.com/ruby/ruby/pull/4291