Project

General

Profile

Actions

Misc #22192

closed

Clarify the scope of default visibility changed inside a block

Misc #22192: Clarify the scope of default visibility changed inside a block

Added by shugo (Shugo Maeda) 6 days ago. Updated 6 days ago.


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 Actions #1 [ruby-core:126067]

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 Actions #2 [ruby-core:126069]

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 1Actions #3 [ruby-core:126078]

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 Actions #4 [ruby-core:126079]

  • 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.

Actions

Also available in: PDF Atom