Bug #22076
closeddefined? returns nil for protected methods defined in a module even when callable
Description
Filing this to confirm whether defined? should return "method" here (matching the call check), or whether the current nil behavior is intentional.
Summary¶
defined? returns nil for a protected method defined in a module, even when the same call succeeds.
For a protected method defined in a class, defined? and the call agree.
Reproduction¶
module Mix
def secret; 42; end
protected :secret
end
class A; include Mix; end
class B
include Mix
def call_it(other) = other.secret
def defined_it(other) = defined?(other.secret)
end
p B.new.call_it(A.new) # => 42
p B.new.defined_it(A.new) # => nil (expected "method")
The class-defined version returns "method" for the same shape:
class Base
def secret; 42; end
protected :secret
end
class A < Base; end
class B < Base
def defined_it(other) = defined?(other.secret)
end
p B.new.defined_it(A.new) # => "method"
Fix¶
Use rb_callable_method_entry_with_refinements and the existing
vm_defined_class_for_protected_call helper. Patch with test:
https://github.com/ruby/ruby/pull/17069
Environment¶
- MacOS Tahoe 26.2
- Ruby master (HEAD)
- ruby 4.0.1
Updated by sampokuokkanen (Sampo Kuokkanen) about 8 hours ago
- Description updated (diff)
Updated by sampokuokkanen (Sampo Kuokkanen) about 8 hours ago
- ruby -v set to ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25]
Updated by nobu (Nobuyoshi Nakada) about 8 hours ago
- Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED
The expectation and the fix seem reasonable both.
Updated by sampokuokkanen (Sampo Kuokkanen) about 7 hours ago
- Status changed from Open to Closed
Applied in changeset git|d1391a000359655f948e16d7f2b0e85890082b5a.
Fix defined? for protected methods defined in a module
me->defined_class is 0 for methods stored on a module, so the
protected visibility check in vm_defined always failed and defined?
returned nil even when the call would succeed.
Use rb_callable_method_entry_with_refinements (where defined_class is
populated) and the same vm_defined_class_for_protected_call helper as
the call path. defined? now agrees with whether the method could
actually be called.
[Bug #22076]