Bug #16618
closedEnsure called twice when raise in ensure
Description
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) almost 5 years ago
- Subject changed from Rescue called twice when raise in ensure to Ensure called twice when raise in ensure
Updated by jeremyevans0 (Jeremy Evans) almost 5 years ago
- Related to Bug #13930: Exception is caught in rescue above ensure added
Updated by davidsiaw (David Siaw) almost 5 years 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) almost 4 years 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.
iter do
next
rescue
R
ensure
raise
end
In this case, R should not be executed, but executed without this patch.
Fixes [Bug #13930]
Fixes [Bug #16618]
A part of tests are written by @jeremyevans https://github.com/ruby/ruby/pull/4291