Skip to content

Raise LocalJumpError if returning from proc inside lambda inside method outside of the lambda #4223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

jeremyevans
Copy link
Contributor

Previously, return in a proc inside a lambda inside a method could
return to two separate places. If the return occurred while still
inside the lambda, it returned to the lambda. If the return occured
while outside the lambda, it returned to the method.

Fix this by checking the parent iseqs of the proc for a lambda proc,
and recording the iseq. When processing the callstack looking for
the return target, if you don't find a lambda proc or you find a
different lambda proc that the expected lambda proc, it means the
lambda proc has already returned, and therefore the proc return
should raise a LocalJumpError.

This currently adds a param flag on the iseq for whether the iseq is
a lambda proc, since I cannot figure out how to get the proc object
from the parent iseq to check whether it is a lambda proc.

Fixes [Bug #17105]

…od outside of the lambda

Previously, return in a proc inside a lambda inside a method could
return to two separate places.  If the return occurred while still
inside the lambda, it returned to the lambda.  If the return occured
while outside the lambda, it returned to the method.

Fix this by checking the parent iseqs of the proc for a lambda proc,
and recording the iseq.  When processing the callstack looking for
the return target, if you don't find a lambda proc or you find a
different lambda proc that the expected lambda proc, it means the
lambda proc has already returned, and therefore the proc return
should raise a LocalJumpError.

This currently adds a param flag on the iseq for whether the iseq is
a lambda proc, since I cannot figure out how to get the proc object
from the parent iseq to check whether it is a lambda proc.

Fixes [Bug #17105]
@jeremyevans jeremyevans requested a review from ko1 February 25, 2021 02:22
Copy link
Contributor

@headius headius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot review the C code portions of this, but I can confirm that the added test cases match JRuby behavior we fixed in jruby/jruby#6351 using a similar strategy.

@ko1
Copy link
Contributor

ko1 commented Mar 29, 2021

Sorry for long absent.
This patch is not acceptable because it changes the ISEQ information globally.

def foo(maker)
  pr = send(maker){
    proc{ return :inner }
  }
  innner_pr = pr[]
  innner_pr[]
  :never
end

begin
  p foo(:lambda)
rescue => e
  p e
end

begin
  p foo(:proc)
rescue => e
  p e
end
#<LocalJumpError: unexpected return>
:inner

is expected but

#<LocalJumpError: unexpected return>
#<LocalJumpError: unexpected return>

@jeremyevans
Copy link
Contributor Author

@ko1 Is it possible to fix this issue with the current VM design? I couldn't determine how to get access to the proc/lambda at the point that the return is processed, and I'm guessing the reason for that is there could be more than one (as shown in your example) and the VM wouldn't know which.

@ko1
Copy link
Contributor

ko1 commented Apr 1, 2021

I find the way to implement this spec with current information, but it is complicated....

@ko1
Copy link
Contributor

ko1 commented Apr 1, 2021

#4347

@ko1 ko1 closed this Apr 1, 2021
ko1 added a commit to ko1/ruby that referenced this pull request Apr 1, 2021
A "return" statement in a Proc in a lambda like:
  `lambda{ proc{ return }.call }`
should return outer lambda block. However, the inner Proc can become
orphan Proc from the lambda block. This "return" escape outer-scope
like method, but this behavior was decieded as a bug.
[Bug #17105]

This patch raises LocalJumpError by checking the proc is orphan or
not from lambda blocks before escaping by "return".

Most of tests are written by Jeremy Evans
ruby#4223
ko1 added a commit that referenced this pull request Apr 2, 2021
A "return" statement in a Proc in a lambda like:
  `lambda{ proc{ return }.call }`
should return outer lambda block. However, the inner Proc can become
orphan Proc from the lambda block. This "return" escape outer-scope
like method, but this behavior was decieded as a bug.
[Bug #17105]

This patch raises LocalJumpError by checking the proc is orphan or
not from lambda blocks before escaping by "return".

Most of tests are written by Jeremy Evans
#4223
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants