From: "Eregon (Benoit Daloze)" Date: 2022-05-26T19:44:57+00:00 Subject: [ruby-core:108715] [Ruby master Bug#18806] protected methods defined by refinements can't be called Issue #18806 has been updated by Eregon (Benoit Daloze). I agree this looks like unintended behavior/a bug. TruffleRuby already behaves like: ``` :refined :refined :refined :refined "method" ``` fcalls should ignore visibility, always, so I think 2. is best. ---------------------------------------- Bug #18806: protected methods defined by refinements can't be called https://bugs.ruby-lang.org/issues/18806#change-97766 * Author: jhawthorn (John Hawthorn) * Status: Open * Priority: Normal * ruby -v: ruby 3.2.0dev * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- Hello! The `protected` visibility is a bit unusual, since it depends on where the callee method is considered to be defined. I was looking into making [an optimization](https://github.com/ruby/ruby/pull/5643) to calling `protected` methods on self, and we came across some surprising behaviour. (Thanks @alanwu for finding!) As far as I can tell, there is no way to call a method refined method is `protected` visibility (other than `send`). ``` ruby class A end module MyRefine refine(A) { private def private_foo = :refined def private_foo_in_refinement = private_foo protected def protected_foo = :refined def protected_foo_in_refinement = protected_foo } end class A using MyRefine def call_private = private_foo def call_private_through_refinement = private_foo_in_refinement def call_protected = protected_foo def call_protected_through_refinement = protected_foo_in_refinement def is_defined = defined?(protected_foo) end A.new.call_private # => :refined A.new.call_private_through_refinement # => :refined A.new.call_protected # => NoMethodError: protected method `protected_foo' called for # A.new.call_protected_through_refinement # => NoMethodError: protected method `protected_foo' called for # A.new.is_defined # "method" ``` I find it confusing that here protected is more restrictive than private (private methods from a refinement can be called as normal), but I'm not sure if it's a rule that it should be or just how I've always assumed it is. It's also odd that `defined?` returns truthy, but I think this might actually be a bug on defined with protected methods (behaviour doesn't match that of method calls). I think this is probably not intentional or desired behaviour. It's not useful for us to make methods which can't be called. The reason this happens I think is an implementation detail: the "defined class" of the method we're trying to call is the ICLASS including the refinement module onto the `A` superclass. Possible options I see for improving this: 1. Treat defined methods as though they were defined on the refined class. Both examples above will now work, and it is possible to call the refined method on objects other than self. 2. Always allow "fcalls" to `protected` methods. Making them basically an alias for `private` when used in a refinement. 3. Forbid using `protected` inside a refinement (and `Refinement#import_methods`), raising an error. My preference would be 1. I think it would require one extra check when calling protected methods, but calling protected methods is already a slower path so I think it is fine. Thanks for reading! -- https://bugs.ruby-lang.org/ Unsubscribe: