Skip to content
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

break within a block throws LocalJumpError in JRuby but succeeds in MRI Ruby #4369

Closed
amarkowitz opened this issue Dec 8, 2016 · 6 comments
Closed

Comments

@amarkowitz
Copy link

@amarkowitz amarkowitz commented Dec 8, 2016

Environment

user_name@ ~/dev/scripts$ jruby --version
jruby 9.1.5.0 (2.3.1) 2016-09-07 036ce39 Java HotSpot(TM) 64-Bit Server VM 25.112-b16 on 1.8.0_112-b16 +jit [darwin-x86_64]
user_name@ ~/dev/scripts$ uname -a
Darwin MACHINE_NAME 16.1.0 Darwin Kernel Version 16.1.0: Wed Oct 19 20:31:56 PDT 2016; root:xnu-3789.21.4~4/RELEASE_X86_64 x86_64

Test Code

def lambda_calls_block
  lambda { yield }.call
end

f = nil
lambda_calls_block do
  f = 'a'
  break
end

puts f

Expected Behavior

I expect the above code to print a to the console.

Actual Behavior

MRI Ruby 2.3.0

user_name@ ~/dev/scripts$ rvm use ruby-2.3.0
Using /Users/user/.rvm/gems/ruby-2.3.0
user_name@ ~/dev/scripts$ ruby break_probs.rb 
a

JRuby behavior in 9.1.6.0, 9.1.5.0 and 1.7.19 is to throw a LocalJumpError:

user_name@ ~/dev/scripts$ rvm use jruby-9.1.6.0
Using /Users/user/.rvm/gems/jruby-9.1.6.0
user_name@ ~/dev/scripts$ ruby break_probs.rb 
LocalJumpError: unexpected break
  block in lambda_calls_block at break_probs.rb:2
           lambda_calls_block at break_probs.rb:2
                       <main> at break_probs.rb:6
user_name@ ~/dev/scripts$ rvm use jruby-9.1.5.0
Using /Users/user/.rvm/gems/jruby-9.1.5.0
user_name@ ~/dev/scripts$ ruby break_probs.rb 
LocalJumpError: unexpected break
  block in lambda_calls_block at break_probs.rb:2
           lambda_calls_block at break_probs.rb:2
                       <main> at break_probs.rb:6
user_name@ ~/dev/scripts$ rvm use jruby-1.7.19
Using /Users/user/.rvm/gems/jruby-1.7.19
user_name@ ~/dev/scripts$ ruby break_probs.rb 
LocalJumpError: unexpected break
                call at org/jruby/RubyProc.java:271
  lambda_calls_block at break_probs.rb:2
              (root) at break_probs.rb:6
@headius
Copy link
Member

@headius headius commented Dec 8, 2016

I'm surprised this works on MRI. I was under the impression that break can never escape a lambda body.

Confirmed on master:

[] ~/projects/jruby $ ruby23 -e "def foo; lambda { yield }.call; end; foo { break }"

[] ~/projects/jruby $ jruby -e "def foo; lambda { yield }.call; end; foo { break }"
LocalJumpError: unexpected break
...

Note that it works fine if you change the lambda to a proc. I suspect we're just preventing all non-local flow control from escaping a proper lambda.

This may be an IR thing or just a Proc thing.

cc @subbuss @enebo

@headius
Copy link
Member

@headius headius commented Dec 8, 2016

This works correctly in JRuby 1.7.25.

@drstrangelooker
Copy link

@drstrangelooker drstrangelooker commented Dec 8, 2016

The commit that was possibly regressed was between 1.7.19 and 1.7.20.

$ rvm use jruby-1.7.20
Using /home/miked/.rvm/gems/jruby-1.7.20
$ ruby -v
jruby 1.7.20 (1.9.3p551) 2015-05-04 3086e6a on Java HotSpot(TM) 64-Bit Server VM 1.8.0_112-b15 +jit [linux-amd64]
$ ruby -e "def foo; lambda { yield }.call; end; foo { break }"

$ rvm use jruby-1.7.19
Using /home/miked/.rvm/gems/jruby-1.7.19
$ ruby -v
jruby 1.7.19 (1.9.3p551) 2015-01-29 20786bd on Java HotSpot(TM) 64-Bit Server VM 1.8.0_112-b15 +jit [linux-amd64]
$ ruby -e "def foo; lambda { yield }.call; end; foo { break }"
LocalJumpError: unexpected break
    call at org/jruby/RubyProc.java:271
     foo at -e:1
  (root) at -e:1
@drstrangelooker
Copy link

@drstrangelooker drstrangelooker commented Dec 8, 2016

I think this is the commit that originally fixed it, but then the code was refactored for 9k. e61b737

@subbuss
Copy link
Contributor

@subbuss subbuss commented Dec 13, 2016

} else if ((exc instanceof IRBreakJump) && inNonMethodBodyLambda(scope, blockType)) {
// We just unwound all the way up because of a non-local break
context.setSavedExceptionInLambda(IRException.BREAK_LocalJumpError.getException(context.runtime));
has logic that always triggers a LocalJumpError for non-local breaks (which a break from a yield is considered) in a lambda. Maybe this logic is stale? I cannot keep this straight anymore since it has been a long time, but are there scenarios where breaks in lambdas trigger a local jump error?

@enebo
Copy link
Member

@enebo enebo commented Dec 13, 2016

I cannot find any case where lambda cannot contain a break. If someone can chime in on an error case it would be great.

headius added a commit to headius/jruby that referenced this issue Jan 6, 2017
@headius headius added this to the JRuby 9.1.7.0 milestone Jan 6, 2017
@enebo enebo closed this Jan 10, 2017
enebo added a commit that referenced this issue Jan 11, 2017
Kill the LocalJumpError logic for break in lambda. Fixes #4369.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.