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

Comments

Projects
None yet
5 participants
@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

This comment has been minimized.

Show comment
Hide comment
@headius

headius Dec 8, 2016

Member

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

Member

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

This comment has been minimized.

Show comment
Hide comment
@headius

headius Dec 8, 2016

Member

This works correctly in JRuby 1.7.25.

Member

headius commented Dec 8, 2016

This works correctly in JRuby 1.7.25.

@deangelo-llooker

This comment has been minimized.

Show comment
Hide comment
@deangelo-llooker

deangelo-llooker 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

deangelo-llooker 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
@deangelo-llooker

This comment has been minimized.

Show comment
Hide comment
@deangelo-llooker

deangelo-llooker Dec 8, 2016

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

deangelo-llooker commented Dec 8, 2016

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

@subbuss

This comment has been minimized.

Show comment
Hide comment
@subbuss

subbuss Dec 13, 2016

Contributor

} 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?

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@enebo

enebo Dec 13, 2016

Member

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

Member

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

Merge pull request #4423 from headius/lambda-break
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