From: "hsbt (Hiroshi SHIBATA) via ruby-core" Date: 2026-09-18T00:22:29+00:00 Subject: [ruby-core:126765] [Ruby Bug#22332] Ruby::Box: prelude is evaluated only in the master box, so pp and binding.irb load into it Issue #22332 has been reported by hsbt (Hiroshi SHIBATA). ---------------------------------------- Bug #22332: Ruby::Box: prelude is evaluated only in the master box, so pp and binding.irb load into it https://bugs.ruby-lang.org/issues/22332 * Author: hsbt (Hiroshi SHIBATA) * Status: Open * Assignee: tagomoris (Satoshi Tagomori) * ruby -v: ruby 4.1.0dev (2026-09-17T00:01:17Z master 3f6143715a) [arm64-darwin27] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Under `RUBY_BOX=1`, `Kernel#pp` prints but leaves nothing behind in the box that called it. ``` $ RUBY_BOX=1 ruby --disable-gems -W:no-experimental -e 'pp({a: 1}); p $LOADED_FEATURES.grep(/pp\.rb/).size, defined?(PP)' {a: 1} 0 nil $ ruby --disable-gems -e 'pp({a: 1}); p $LOADED_FEATURES.grep(/pp\.rb/).size, defined?(PP)' {a: 1} 1 "constant" ``` Requiring it afterwards therefore loads the same file a second time, and it redefines its own methods: ``` $ RUBY_BOX=1 ruby --disable-gems -W:no-experimental -e 'pp({a: 1}); require "pp"' {a: 1} .../lib/ruby/4.1.0+4/pp.rb:412: warning: method redefined; discarding old pretty_print .../lib/ruby/4.1.0+4/pp.rb:412: warning: previous definition of pretty_print was here (and 20 more) ``` A box created before the call cannot see it either, while one created afterwards inherits it from the master box: ``` $ RUBY_BOX=1 ruby --disable-gems -W:no-experimental -e 'before = Ruby::Box.new; pp({a: 1}); after = Ruby::Box.new; p before.eval("defined?(PP)"), after.eval("defined?(PP)")' {a: 1} nil "constant" ``` `Binding#irb` fails outright, because its `rescue LoadError, Gem::LoadError` clause is evaluated where RubyGems is not loaded: ``` $ RUBY_BOX=1 ruby -W:no-experimental -e 'binding.irb' < /dev/null -e:1:in 'Binding#irb': uninitialized constant Binding::Gem (NameError) ``` Without a box the same command starts irb. `prelude.rb` is evaluated once in `rb_call_builtin_inits()` (`Init_builtin_prelude()`, `inits.c:114`), before the root and main boxes exist, so the box recorded on `Kernel#pp` and `Binding#irb` is the master box, and the `require` they run loads there. `gem_prelude` already avoids this: `rb_load_gem_prelude()` (`builtin.c:114`) runs for root and main in `Init_builtin_features()` and again for every new box (`box.c:435`). So the question is where prelude should be evaluated. Three shapes I can see: - load prelude per box the way gem_prelude already is (https://github.com/ruby/ruby/pull/18713); - keep the single evaluation and mark the prelude methods `Primitive.attr! :caller_user_box`, so only their `require` resolves in the caller's box; - make `require` called from a master-box method resolve in the caller's box in general, which needs care not to become local rebinding (#21362). Is there another shape you would prefer? Reproduced on master 3f6143715a and on released 4.0.7. Related to #22275 -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/