Skip to content

JRuby 10.0/CRuby 3.4+ compat: assignment to an outer local variable named it inside a block seems to assume/create a block level shadow #9651

Description

@chadlwilson

Environment Information

  • Broken: jruby 10.0.6.0 (3.4.5) and jruby 10.1.1.0 (4.0.0), arm64-darwin, OpenJDK 25
  • Correct: jruby 9.4.15.0 (3.1 language level, predates it), CRuby 3.4.8 and 4.0.6

Expected Behavior

Assigning to a local variable named it from inside a block does not write the outer variable as it does on CRuby. Instead, JRuby seems to create a fresh block-local it, so writes are silently lost and op-assignments (it += 1) raise NoMethodError: undefined method '+' for nil.

Reads of an outer it work correctly (fixed by #8767 in 10.0.1.0); only assignment seems broken. It seems broken even when the block declares an explicit parameter, where it cannot be the implicit parameter at all.

Actual Behavior

it = 0; 3.times { it = 5 }; p it          # CRuby: 5    JRuby: 0 (silent wrong value)
it = 0; 3.times { it += 1 }; p it         # CRuby: 3    JRuby: NoMethodError: undefined method '+' for nil
it = 0; [1,2].each { |e| it += e }; p it  # CRuby: 3    JRuby: NoMethodError (explicit block param!)
it = 0; 3.times { p it }                  # CRuby: 0 0 0  JRuby: 0 0 0 (reads OK)

Or second example locally:

$ for v in jruby-9.4 jruby-10.0 jruby-10.1 3.1 3.4 4.0; do
for>   echo "=== ruby@$v"
for>   mise exec "ruby@$v" -- ruby -v -e 'it = 0; 3.times { it += 1 }; p it' || echo "FAILED (exit $?)"
for> done
=== ruby@jruby-9.4
jruby 9.4.15.0 (3.1.7) 2026-06-08 6114208a31 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +jit [arm64-darwin]
3
=== ruby@jruby-10.0
jruby 10.0.6.0 (3.4.5) 2026-06-11 716ad51f54 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +indy +jit [arm64-darwin]
NoMethodError: undefined method '+' for nil
  <main> at -e:1
   times at org/jruby/RubyFixnum.java:338
  <main> at -e:1
FAILED (exit 1)
=== ruby@jruby-10.1
jruby 10.1.1.0 (4.0.0) 2026-07-22 aaa95d57e1 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +indy +jit [arm64-darwin]
NoMethodError: undefined method '+' for nil
  <main> at -e:1
   times at org/jruby/RubyFixnum.java:610
  <main> at -e:1
FAILED (exit 1)
=== ruby@3.1
ruby 3.1.7p261 (2025-03-26 revision 0a3704f218) [arm64-darwin25]
3
=== ruby@3.4
ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [arm64-darwin25]
3
=== ruby@4.0
ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +PRISM [arm64-darwin23]
3

Real-world impact

Claude-assisted analysis/scoping.

sorbet-runtime 0.6.x uses it as a loop counter in lib/types/private/methods/signature.rb (it = 0 ... rest_count.times { ...; it += 1 }), so runtime validation of any sig'd splat-arg method crashes.

https://github.com/sorbet/sorbet/blob/e8f0eb82de923877387fa02fd6bbb2aac4801a66/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L228-L236

Via js-routes (which depends on sorbet-runtime), this can break rake assets:precompile for Rails apps on JRuby 10.

Workaround for sorbet-runtime users: SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL=never (not the same behaviour I guess, I am not personally particularly familiar with Sorbet).

Related issues

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions