Misc #22192
closedClarify the scope of default visibility changed inside a block
Description
private etc. called with no arguments inside a block changes the default visibility of the enclosing lexical scope, and the change survives the block:
class C
%i[foo bar].each do |name|
private
define_method(name) { }
end
def afterwards; end # also private
end
C.private_method_defined?(:foo) #=> true
C.private_method_defined?(:afterwards) #=> true
When multiple Procs share the same lexical scope, private in a Proc affects all Procs:
class Host
SET_PRIVATE = proc { private }
DEFINE = proc { def probe; end }
end
Host::SET_PRIVATE.call
Host::DEFINE.call
Host.private_method_defined?(:probe) #=> true
Neither CRuby nor ruby/spec has corresponding tests. I assume this is intentional, but am I correct?
It seems that the visibility change is effective only within the block on JRuby, so I'd like to clarify this.
Updated by shugo (Shugo Maeda) 6 days ago
· Edited
I checked the behavior of past versions with all-ruby:
$ docker run -it --rm rubylang/all-ruby ./all-ruby -e 'class C; [1].each { private; def foo; end }; def afterwards; end; end; p [C.private_instance_methods(false).sort, C.public_instance_methods(false).sort]'
...
ruby-1.1b9_06 -e:1: undefined method `public_instance_methods' for C (NameError)
exit 1
ruby-1.1b9_07 [["afterwards", "foo"], []]
...
ruby-1.6.8 [["afterwards", "foo"], []]
ruby-1.8.0 [["foo"], ["afterwards"]]
...
ruby-1.8.7-p374 [["foo"], ["afterwards"]]
ruby-1.9.0-0 [[:afterwards, :foo], []]
...
ruby-4.0.5 [[:afterwards, :foo], []]
| Versions | Behavior |
|---|---|
| 1.1b9 - 1.6.8 | same as the current behavior |
| 1.8.0 - 1.8.7 | same as JRuby (reverted at the end of a block) |
| 1.9.0 - 4.0.5 | same as the current behavior |
Updated by shugo (Shugo Maeda) 6 days ago
ISO/IEC 30170 also specifies that the default visibility change survives the block (a block call pushes only local variable bindings; the default visibility stack is untouched).
Updated by Eregon (Benoit Daloze) 6 days ago
TruffleRuby also behaves like CRuby here.
My understanding is method visibility is some state that only exists in methods and not in blocks (same as $~, etc).
There should already be some ruby/spec for that.
Updated by shugo (Shugo Maeda) 6 days ago
· Edited
- Status changed from Assigned to Closed
Eregon (Benoit Daloze) wrote in #note-3:
TruffleRuby also behaves like CRuby here.
My understanding is method visibility is some state that only exists in methods and not in blocks (same as$~, etc).There should already be some ruby/spec for that.
Thank you, I've found the following spec in spec/ruby/core/module/shared/set_visibility.rb:
it "sets the visibility outside the closure" do
visibility = @method
mod = Module.new {
1.times {
send visibility
}
def test1() end
}
mod.send(:"#{@method}_instance_methods", false).should.include?(:test1)
end
JRuby is aware of the incompatibility: it tags this spec as failing for Module#private and Module#protected.
So, I'm closing this issue.