Issue #15114 has been updated by osyo (manga osyo).


こちら修正してみたので再度ご確認をお願いします。
手元では `make DEFS=-DVM_CHECK_MODE=2 check` のテストは通りました。

pull request : https://github.com/ruby/ruby/pull/2039

----------------------------------------
Bug #15114: Ruby で定義したメソッドに `&:hoge` を渡しても refinements が有効にならない
https://bugs.ruby-lang.org/issues/15114#change-75669

* Author: osyo (manga osyo)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: 
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
## 概要

Ruby 2.4 で以下のように『refinements で追加したメソッドが `&:twice` で呼び出せる』ようになりました。

```ruby
# 新しく String#twice を refinements で追加
using Module.new {
	refine String do
		def twice
			self + self
		end
	end
}

# OK
p ["homu", "mami", "mado"].map { |it| it.twice }
# => ["homuhomu", "mamimami", "madomado"]

# 2.4 以前では
# Error: `map': undefined method `twice' for "homu":String (NoMethodError)
# とエラーになっていたが、2.4 から Symbol#to_proc 内からでも refinements で定義されたメソッドが呼び出せるようになった
p ["homu", "mami", "mado"].map(&:twice)
# => ["homuhomu", "mamimami", "madomado"]
```

https://wandbox.org/permlink/EClJQZ9vXZfN1T7H


## 問題点

しかし、次のように Ruby で定義したメソッドに対して `&:twice` を渡しても反映されません。

```ruby
class X
	def meth &block
		block.call "homu"
	end
end

using Module.new {
	refine String do
		def twice
			self + self
		end
	end
}

# OK
p X.new.meth { |it| it.twice }
# => "homuhomu"

# Error: `meth': undefined method `twice' for "homu":String (NoMethodError)
p X.new.meth(&:twice)
```

https://wandbox.org/permlink/0OnXOQyVluZ6Sjgc


これは、以下のように自作クラスで `include Enumerable` した際に `Array#map` と同様に『refinements で追加したメソッドを `&:twice` で呼び出したい場合』に問題になります。

```ruby
class MyArray
	include Enumerable

	def initialize ary
		@ary = ary
	end

	def each &block
		@ary.each &block
	end
end

using Module.new {
	refine String do
		def twice
			self + self
		end
	end
}

ary = MyArray.new ["homu", "mami", "mado"]

# Error: `each': undefined method `twice' for "homu":String (NoMethodError)
p ary.map(&:twice)
```

https://wandbox.org/permlink/8xbyN3xpXXG46wCa

また、類似の問題として `Symbol#to_proc` から直接 `Proc` を生成し、呼び出した場合も refinements が反映されません。

```ruby
using Module.new {
	refine String do
		def twice
			self + self
		end
	end
}

# Error: undefined method `twice' for "homu":String (NoMethodError)
:twice.to_proc.call "homu"
```

https://wandbox.org/permlink/KU3KWcjJ9kabpNtX


## 修正内容

ひとまず `meth &:twice` に対して修正を行い、それのパッチを添付してあります。
修正内容としては `metho &:twice` を呼び出して、[`Proc` オブジェクトを生成する時](https://github.com/osyo-manga/ruby/blob/69014e25ded0b4cf6f268983e5450557c6d64ec6/vm_args.c#L888)に `cref->refinements` を `refine_sym_proc_call` のコールバック引数に追加し、`Proc#call` 時に呼ばれる [`refine_sym_proc_call` 内で `cref->refinements` を参照してメソッド探査する](https://github.com/osyo-manga/ruby/blob/69014e25ded0b4cf6f268983e5450557c6d64ec6/vm_args.c#L862)ようにしました。
修正する方向性として問題ないようであれば `Symbol#to_proc` から呼び出した場合に対しても修正パッチを書こうと考えています。


## 修正内容に対する問題点

ひとまず無理やり動作させるようにしてみたのですが、以下の点が気になっています。

* `cref->refinements` の寿命がわからない
  * `Proc#call` を呼び出した時点で死んでいる可能性があるかも?
* `Proc` オブジェクトのキャッシュ化を無効
  * これは以下のようなケースでキャッシュ化を有効にしていると正しく動作しないので無効にしました…

```ruby
def meth &block
	block.call "homu"
end

using Module.new {
	refine String do
		def twice
			self + self
		end
	end
}

p meth &:twice
# => "homuhomu"


# String#twice を再定義する
using Module.new {
	refine String do
		def twice
			self + self + self
		end
	end
}

# ここで新たに proc オブジェクトを生成してほしい
p meth &:twice
# => "homuhomuhomu"
```

修正内容や方向性に対して意見があればコメントして頂けると助かります。




---Files--------------------------------
fix_symbol_to_proc.patch (3.57 KB)


-- 
https://bugs.ruby-lang.org/