Bug #15114
closedRuby で定義したメソッドに `&:hoge` を渡しても refinements が有効にならない
Description
概要¶
Ruby 2.4 で以下のように『refinements で追加したメソッドが &:twice
で呼び出せる』ようになりました。
# 新しく 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
を渡しても反映されません。
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
で呼び出したい場合』に問題になります。
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 が反映されません。
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
オブジェクトを生成する時に cref->refinements
を refine_sym_proc_call
のコールバック引数に追加し、Proc#call
時に呼ばれる refine_sym_proc_call
内で cref->refinements
を参照してメソッド探査するようにしました。
修正する方向性として問題ないようであれば Symbol#to_proc
から呼び出した場合に対しても修正パッチを書こうと考えています。
修正内容に対する問題点¶
ひとまず無理やり動作させるようにしてみたのですが、以下の点が気になっています。
-
cref->refinements
の寿命がわからない-
Proc#call
を呼び出した時点で死んでいる可能性があるかも?
-
-
Proc
オブジェクトのキャッシュ化を無効- これは以下のようなケースでキャッシュ化を有効にしていると正しく動作しないので無効にしました…
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
Updated by matz (Yukihiro Matsumoto) about 6 years ago
- Tracker changed from Feature to Bug
- Backport set to 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
Updated by nobu (Nobuyoshi Nakada) about 6 years ago
キャッシュ化を無効にしなくてもテストは通るようですが、これはテストが意図したとおりになっていないということでしょうか。
Updated by osyo (manga osyo) about 6 years ago
キャッシュ化を無効化していない場合は test_to_proc_arg_with_refinements_override
で落ちるはずです。
後ほどこちらでも確認してみます。
Updated by nobu (Nobuyoshi Nakada) about 6 years ago
- Status changed from Open to Closed
Updated by osyo (manga osyo) about 6 years ago
対応遅れてしまいすみません。
ありがとうございます。
Updated by nobu (Nobuyoshi Nakada) about 6 years ago
- Status changed from Closed to Open
テストでクラッシュするため、一旦revertしました。
Updated by osyo (manga osyo) about 6 years ago
nobu (Nobuyoshi Nakada) wrote:
テストでクラッシュするため、一旦revertしました。
パッチを作成してからだいぶ時間が経っていたので手元で rebase trunk
して最新版を pull request してみましたがこちらは CI が通っていますね。
pull request : https://github.com/ruby/ruby/pull/2039
Updated by nobu (Nobuyoshi Nakada) about 6 years ago
make DEFS=-DVM_CHECK_MODE=2 check
で落ちます。
Updated by osyo (manga osyo) about 6 years ago
ありがとうございます。
こちらでも再現できたのでもう少し調査してみます。
Updated by osyo (manga osyo) almost 6 years ago
こちら、わたしの方でも調査しているんですが、ちょっと時間がかかりそうなので、引き続きわたしの方でも調査しますが詳しい方に見てもらえると助かります。
Updated by osyo (manga osyo) almost 6 years ago
こちら修正してみたので再度ご確認をお願いします。
手元では make DEFS=-DVM_CHECK_MODE=2 check
のテストは通りました。
pull request : https://github.com/ruby/ruby/pull/2039
Updated by nobu (Nobuyoshi Nakada) almost 6 years ago
- Status changed from Open to Closed
Applied in changeset trunk|r66439.
Enable refinements on symbol-proc in ruby-level methods
-
vm_args.c (refine_sym_proc_call): resolve refinements when the
proc is invoked, instead of resolving at making the proc, to
enable refinements on symbol-proc in ruby-level methods -
vm.c (vm_cref_dup): clear cached symbol-procs when duplicating.
[Bug #15114] [Fix GH-2039]
From: manga_osyo manga.osyo@gmail.com