From: "shugo (Shugo Maeda) via ruby-core" Date: 2026-08-28T09:33:57+00:00 Subject: [ruby-core:126536] [Ruby Bug#22273] Aliasing doesn't interact well with Module#prepend Issue #22273 has been updated by shugo (Shugo Maeda). I agree with Jeremy, and would like to point out that Ruby already has exactly this semantics for refinements. If the prepend in the example is replaced with a refinement, we get the expected behavior: ```ruby using Module.new { refine Kernel do def require(feature) puts "requiring feature (refine): #{feature}" super end end } module Kernel alias original_require require def require(feature) puts "requiring feature (alias): #{feature}" original_require(feature) end end require "set" ``` ``` requiring feature (refine): set requiring feature (alias): set # and the original require succeeds ``` `alias` never sees the refinement layer. If the method exists only in the refinement, `alias` raises `NameError`: ```ruby class C; end using Module.new { refine(C) { def m = :refined } } class C alias m2 m #=> NameError: undefined method 'm' for class 'C' end ``` This is precisely what Jeremy proposes for prepend: a module can alias only its own methods and its ancestors' methods, and aliasing a method that exists only in a prepended module raises `NameError`. Since a refinement and a prepended module play the same role (a layer in front of the class's own methods, whose `super` reaches the class's own method), I think prepend should behave the same way as refinements here. Another argument: the current behavior is already inconsistent between classes and modules. The same pattern as in the ticket applied to a class does not raise `NoMethodError` but loops forever: ```ruby class A; def m = [:A]; end class B < A prepend(Module.new { def m = [:P, *super] }) alias orig m def m = [:new, *orig] end B.new.m #=> SystemStackError ``` With the module version, the same code raises `NoMethodError` instead. This comes from how the defined class of the aliased method is resolved for `super` (`rb_find_defined_class_by_owner`). If `alias` looked up the method from the origin class, both cases would simply alias the class's own method, and the difference would disappear. One thing to be careful about when implementing this: if the target is a module and the method is not found, `rb_alias` falls back to searching from `Object`. So even if `alias` looked up the method from the origin class, a method that exists only in a module prepended to `Kernel` would still be found through `Object`'s ancestors when aliased in `module Kernel`. ---------------------------------------- Bug #22273: Aliasing doesn't interact well with Module#prepend https://bugs.ruby-lang.org/issues/22273#change-118721 * Author: luke-gru (Luke Gruber) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Currently, aliasing doesn't interact well with `Module#prepend` in my opinion. ### Example ```ruby module Kernel prepend(Module.new do def require(feature) puts "requiring feature (prepend): #{feature}" super end end) end module Kernel alias original_require require def require(feature) puts "requiring feature (alias): #{feature}" original_require(feature) end end require "set" ``` This produces this behavior: ``` requiring feature (prepend): set requiring feature (alias): set requiring feature (prepend): set ../ruby/test.rb:5:in 'require': super: no superclass method 'require' for main (NoMethodError) ``` I would expect this behavior: ``` requiring feature (prepend): set requiring feature (alias): set # Then, the original require would succeed ``` This has caused issues such as [22263](https://bugs.ruby-lang.org/issues/22263) and has [confused gem authors](https://github.com/fxn/zeitwerk/pull/203#issuecomment-1107777206). In the second link, the ignored alias was due to [this bug](https://github.com/ruby/ruby/commit/c59c4d717a2e687972341eab1574196e97d7d7be) which has recently been fixed. ### Bug? As far as I know this is intentional behavior introduced in Ruby 2.0 [here](https://bugs.ruby-lang.org/issues/7842). There are even tests and specs that codify this behavior such as `test_prepend_super_in_alias` and `prepend_spec.rb`. Even though it's intentional, I don't believe it's well thought out. I'm interested in hearing arguments for and against the current behavior (with code examples, preferably). -- 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/