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) 13 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) 13 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) 13 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) 13 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.