[ruby-core:124741] [Ruby Bug#21873] `UnboundMethod#==` returns false for methods obtained via extend + unbind
Issue #21873 has been reported by mdalessio (Mike Dalessio). ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods obtained via extend + unbind https://bugs.ruby-lang.org/issues/21873 * Author: mdalessio (Mike Dalessio) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by mdalessio (Mike Dalessio). A potential fix for this might be: ```diff diff --git a/proc.c b/proc.c index f0cdcae7..67963a1b 100644 --- a/proc.c +++ b/proc.c @@ -2006,6 +2006,8 @@ method_eq(VALUE method, VALUE other) klass1 = method_entry_defined_class(m1->me); klass2 = method_entry_defined_class(m2->me); + if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1); + if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2); if (!rb_method_entry_eq(m1->me, m2->me) || klass1 != klass2 || ``` ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods obtained via extend + unbind https://bugs.ruby-lang.org/issues/21873#change-116343 * Author: mdalessio (Mike Dalessio) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by Eregon (Benoit Daloze). Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED Marking as REQUIRED to backport to all versions since all versions seem affected. ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods from included/extended modules https://bugs.ruby-lang.org/issues/21873#change-116370 * Author: mdalessio (Mike Dalessio) * Status: Closed * Backport: 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by k0kubun (Takashi Kokubun). Backport changed from 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED to 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE ruby_4_0 commit:0768f08caff514b0ba616dc26d76aad90577eefe. ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods from included/extended modules https://bugs.ruby-lang.org/issues/21873#change-116374 * Author: mdalessio (Mike Dalessio) * Status: Closed * Backport: 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by byroot (Jean Boussier). Backport changed from 3.2: REQUIRED, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE to 3.2: WONTFIX, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE 3.2 is in security only maintenance though. ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods from included/extended modules https://bugs.ruby-lang.org/issues/21873#change-116377 * Author: mdalessio (Mike Dalessio) * Status: Closed * Backport: 3.2: WONTFIX, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by luke-gru (Luke Gruber). 3.4 backport PR: https://github.com/ruby/ruby/pull/16326 ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods from included/extended modules https://bugs.ruby-lang.org/issues/21873#change-116613 * Author: mdalessio (Mike Dalessio) * Status: Closed * Backport: 3.2: WONTFIX, 3.3: REQUIRED, 3.4: REQUIRED, 4.0: DONE ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
Issue #21873 has been updated by nagachika (Tomoyuki Chikanaga). Backport changed from 3.2: WONTFIX, 3.3: DONE, 3.4: REQUIRED, 4.0: DONE to 3.2: WONTFIX, 3.3: DONE, 3.4: DONE, 4.0: DONE ruby_3_4: merged at commit:0097b87b1e2c6aa60489527e421e8bf2e2791d69. ---------------------------------------- Bug #21873: `UnboundMethod#==` returns false for methods from included/extended modules https://bugs.ruby-lang.org/issues/21873#change-116630 * Author: mdalessio (Mike Dalessio) * Status: Closed * Backport: 3.2: WONTFIX, 3.3: DONE, 3.4: DONE, 4.0: DONE ---------------------------------------- ## Description `UnboundMethod#==` returns `false` when comparing a module's instance method against the same method obtained via `Method#unbind` on a class that includes or extends that module, despite having the same owner and source location. ```ruby module MyMethods def hello = "hello" end class Base extend MyMethods end from_module = MyMethods.instance_method(:hello) from_unbind = Base.method(:hello).unbind p from_module.owner == from_unbind.owner #=> true p from_module.source_location == from_unbind.source_location #=> true p from_module.inspect == from_unbind.inspect #=> true p from_module == from_unbind #=> false (expected true) ``` ## Diagnosis `method_eq` compares method entries using `method_entry_defined_class`. For methods mixed in via `include`/`extend`, the "defined class" will be an ICLASS and not the original class/module. In the example above, `method_eq` is comparing a module's ICLASS entry against the module, and that will always be false. Related: #18798 (fixed by @ko1 in 59e389af). -- https://bugs.ruby-lang.org/
participants (6)
-
byroot (Jean Boussier) -
Eregon (Benoit Daloze) -
k0kubun (Takashi Kokubun) -
luke-gru (Luke Gruber) -
mdalessio (Mike Dalessio) -
nagachika (Tomoyuki Chikanaga)