[ruby-core:126537] [Ruby Bug#22276] alias in a module falls back to Object even in classes not inheriting from Object
Issue #22276 has been reported by shugo (Shugo Maeda). ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276 * Author: shugo (Shugo Maeda) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
Issue #22276 has been updated by luke-gru (Luke Gruber). I think nothing should be decided on this in isolation. We should discuss this and [22273](https://bugs.ruby-lang.org/issues/22273) together, as they are related as @shugo mentions. ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276#change-118776 * Author: shugo (Shugo Maeda) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
Issue #22276 has been updated by matz (Yukihiro Matsumoto). The root cause is that `alias` in a module treats methods defined in `Object` specially. This made sense in Ruby 1.8, where every class had `Object` as an ancestor, but it contradicts `BasicObject` introduced in 1.9. So I'd like to remove this special treatment instead of resolving the alias at call time (option 2). A module should be able to alias only its own methods and its ancestors' methods, and aliasing any other method should raise `NameError`. I leave the migration path (e.g. a deprecation warning before raising `NameError`) to the implementer. Matz. ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276#change-118888 * Author: shugo (Shugo Maeda) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
Issue #22276 has been updated by shugo (Shugo Maeda). matz (Yukihiro Matsumoto) wrote in #note-3:
So I'd like to remove this special treatment instead of resolving the alias at call time (option 2). A module should be able to alias only its own methods and its ancestors' methods, and aliasing any other method should raise `NameError`.
This is a breaking change, but I agree it is the best behavior.
I leave the migration path (e.g. a deprecation warning before raising `NameError`) to the implementer.
How about the following schedule? 4.1: Verbose-mode deprecation warning 4.2: Non-verbose-mode deprecation warning 4.3: Removal of the special fallback to Object (raise `NameError`) Unlike ruby2_keywords (#22205), I think a documentation-only phase is not needed here, because the migration (e.g., replacing the alias with a method definition that delegates to another method) works the same on all Ruby versions. ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276#change-118892 * Author: shugo (Shugo Maeda) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
Issue #22276 has been updated by shugo (Shugo Maeda). luke-gru (Luke Gruber) wrote in #note-2:
I think nothing should be decided on this in isolation. We should discuss this and [22273](https://bugs.ruby-lang.org/issues/22273) together, as they are related as @shugo mentions.
We discussed this ticket together with #22273 at the developer meeting. Matz decided the direction of this ticket as in #note-3, but he wanted more time to think about #22273. ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276#change-118893 * Author: shugo (Shugo Maeda) * Status: Open * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
Issue #22276 has been updated by shugo (Shugo Maeda). Status changed from Open to Assigned Assignee set to shugo (Shugo Maeda) I've added a verbose-mode warning: https://github.com/ruby/ruby/pull/18740 ---------------------------------------- Bug #22276: alias in a module falls back to Object even in classes not inheriting from Object https://bugs.ruby-lang.org/issues/22276#change-119050 * Author: shugo (Shugo Maeda) * Status: Assigned * Assignee: shugo (Shugo Maeda) * ruby -v: ruby 4.1.0dev (2026-08-28T07:43:16Z master 69b49ac7ae) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When `alias` (or `alias_method`) is used in a module and the method is not found in the module, `alias` searches the method from `Object`. So a module can alias a method of `Kernel` or `Object`, and the alias works even if the module is later included in a class that does not have `Object` and `Kernel` as ancestors: ```ruby module M alias foo puts public :foo end class X < BasicObject include ::M end X.new.foo("hello") #=> hello ``` `X` does not include `Kernel`, but `X.new.foo` calls `Kernel#puts`. This fallback comes from Ruby 1.8, where `Object` was the root class, so every class that includes a module always had `Object` and `Kernel` as ancestors. Since Ruby 1.9 introduced `BasicObject`, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and `Object` is not an ancestor of a module. This behavior is intentional in the current implementation. There are tests for it (`test_alias_in_module` in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of `Module#alias_method` has an example `module Mod; alias_method :orig_exit, :exit; end`. So code like `module M; alias orig_to_s to_s; end` exists, and simply raising `NameError` will break it. Possible fixes: 1. Remove the fallback and raise `NameError`. This breaks existing code. 2. Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of `Object` keeps working, and `X.new.foo` above raises `NoMethodError`. 3. Keep the current behavior and document it. I think 2 is the best choice for compatibility. -- https://bugs.ruby-lang.org/
participants (3)
-
luke-gru (Luke Gruber) -
matz (Yukihiro Matsumoto) -
shugo (Shugo Maeda)