From: "shugo (Shugo Maeda) via ruby-core" Date: 2026-10-22T09:55:23+00:00 Subject: [ruby-core:127210] [Ruby Bug#22462] Method#== can return false for methods that should be considered equal Issue #22462 has been updated by shugo (Shugo Maeda). As I commented in https://github.com/ruby/ruby/pull/19474#pullrequestreview-5609342827, with the PR change, `==` UnboundMethods can bind to non-`==` Methods: ```ruby module M; def m = [:M, *super]; end class Base; def m = [:Base]; end class A < Base; include M; end class B < A; prepend M; def m = [:B, *super]; end b = B.new ua = A.instance_method(:m) ub = B.instance_method(:m) p ua == ub # => true p ua.bind(b) == ub.bind(b) # => true before, false after ``` I suggested there normalizing an iclass to its module in `method_eq()`, but Method objects with different iclasses may behave differently when `super` is called. Another option would be to reconsider [Feature #18798]. If `UnboundMethod#==` compared iclasses, `==` would imply the same behavior, but `ua == M.instance_method(:m)` would also be false, as would `Object.instance_method(:to_s) == Kernel.instance_method(:to_s)`. Treating an UnboundMethod obtained directly from the module as equal to one with any iclass of it would avoid this, but `==` would then not be transitive: `ua == M.instance_method(:m)` and `M.instance_method(:m) == ub` would both be true, while `ua == ub` would be false. ---------------------------------------- Bug #22462: Method#== can return false for methods that should be considered equal https://bugs.ruby-lang.org/issues/22462#change-119625 * Author: jeremyevans0 (Jeremy Evans) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When a `Method` is created using `UnboundMethod#bind` and `#super_method`, the order in which the `#bind` and `#super_method` were applied could result in `Method` objects that were considered unequal. I believe we should have the invariant: for any `UnboundMethod` `um` where `super_method` is not `nil` and `o` is a valid object to bind to `um`, the following code should be true: ```ruby um.bind(o).super_method == um.super_method.bind(o) ``` However, that didn't work correctly in some cases: ```ruby module M; def m = [:M, *super]; end module N; include M; end class C; def m = :C; end class SC < C include N prepend M def m = [:SC, *super] end o = SC.new um = SC.instance_method(:m).super_method.super_method # M#m below SC (included via N) um.bind(o).super_method == um.super_method.bind(o) # => false # should be true ``` There is a similar invariant for Method: for any `Method` `m` where `super_method` is not `nil`, the following code should be true: ```ruby m.unbind.super_method == m.super_method.unbind ``` This appears to already hold, as the existing refinement tests already checked for this behavior. I've submitted a PR to fix this issue: https://github.com/ruby/ruby/pull/19474 -- 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/