Project

General

Profile

Feature #15575

Prohibit to pass a block singleton class

Added by ko1 (Koichi Sasada) almost 2 years ago. Updated almost 2 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
[ruby-core:91333]

Description

The following code works now:

def foo
  class << Object.new
    yield
  end
end

foo{ p :ok } #=> :ok

but I think this feature is very strange because local variables are not active in singleton class.

How about to prohibit this feature?

plan: warning at ruby 2.7 and prohibit it in ruby 3.

Updated by Eregon (Benoit Daloze) almost 2 years ago

Agreed.
IMHO class << expr should be no different than the normal class Name, but currently various things are allowed in that context such as return, yield, etc.

Constant resolution also becomes somewhat dynamically-scoped with class << expr which is also a weird exception.
So longer term maybe it would be good to deprecate class << entirely and use singleton_class.class_exec do instead.

Updated by ko1 (Koichi Sasada) almost 2 years ago

Eregon (Benoit Daloze) wrote:

Agreed.
IMHO class << expr should be no different than the normal class Name, but currently various things are allowed in that context such as return, yield, etc.

Constant resolution also becomes somewhat dynamically-scoped with class << expr which is also a weird exception.
So longer term maybe it would be good to deprecate class << entirely and use singleton_class.class_exec do instead.

or make it syntax sugre of singleton_class.class_exec do?
Both are easy to explain the rule. Now, it is somewhat unclear.

Updated by ko1 (Koichi Sasada) almost 2 years ago

Similar strange example (off-topic):

1.times{
  class C
    break
    def undefined_method; end
  end
  p :unrechable
}
p [:ok, C.instance_methods(false)] #=> [:ok, []]

We can break from class syntax.
I hope nobody use it :(

Updated by Eregon (Benoit Daloze) almost 2 years ago

ko1 (Koichi Sasada) wrote:

or make it syntax sugre of singleton_class.class_exec do?

I think that would be confusing as class (like module and def) is a keyword which normally adds a new lexical scope and does not capture the parent local variables.

Updated by Eregon (Benoit Daloze) almost 2 years ago

ko1 (Koichi Sasada) wrote:

We can break from class syntax.
I hope nobody use it :(

At least TruffleRuby doesn't implement it, and we had no bug report about this, so hopefully not used.
Agreed it should be deprecated/removed as it's confusing at best.

Updated by alanwu (Alan Wu) almost 2 years ago

Another option would be to make everything use lexical scope, if the strangeness is indeed because locals are inaccessible inside class << self.
So imagine the following printing 1 2 3.

foo = 1
class A
  bar = 2
  def hi
    baz = 3
    class << self
      p foo, bar, baz
    end
  end
end
A.new.hi

Is it less strange if everything used the same scoping rule?

Updated by matz (Yukihiro Matsumoto) almost 2 years ago

Agreed to prohibit. Disagree to change the scoping rule (alanwu (Alan Wu)).

Matz.

Updated by ko1 (Koichi Sasada) almost 2 years ago

Matz, Thank you for confirmation.

I add a warning (without -w) like that:

def foo
  class << Object.new
    yield
  end
end

foo{ p :ok } #=> :ok
test.rb: warning: `yield' in class syntax will not be supported from Ruby 3.0. [Feature #15575]

English correction is wlecome :)

#9

Updated by ko1 (Koichi Sasada) almost 2 years ago

  • Status changed from Open to Closed

Applied in changeset trunk|r66999.


check and show a warning for incorrect yield.

  • compile.c (check_yield_place): this function check the yield location.

    • show a warning if yield in class syntax. [Feature #15575]
    • do strict check for toplevel yield. Without this patch, 1.times{ yield } in toplevel is valid-syntax (raise LocalJumpError at runtime) although toplevel simple yield is not valid syntax. This patch make them syntax error.

Also available in: Atom PDF