[Bug #20716] Ruby 2.7 と Ruby 3.x で `instance_method` の挙動が異なっているというバグ報告

2024/09/09に公開

[Bug #20716] Different instance_method behavior in Ruby 2.7 and Ruby 3.x

  • Ruby 2.7 と Ruby 3.x で instance_method の挙動が異なっているというバグ報告
  • 最初に提示されていたのは以下のようなコード
module A
  def test(*args)
    super
  end
end

module B
  def test(a)
    puts a
  end
end

B.prepend(A)

class C
  include B
end

# この呼び出しは問題ない
B.instance_method(:test).bind(C.new).call(1)
# Ruby 2.7: 1
# Ruby 3.0: 1
# Ruby 3.1: 1

# lambda で B#test を再定義すると意図しないエラーになる
a = lambda do
  puts 'lambda'
end

B.module_exec do
  define_method(:test, a)
end

B.instance_method(:test).bind(C.new).call
# Ruby 2.7: lambda
# Ruby 3.0: wrong number of arguments (given 0, expected 1) (ArgumentError)
# Ruby 3.1: wrong number of arguments (given 0, expected 1) (ArgumentError)
  • define_method(:test, a) したときになにか意図しない動作になっているんですかね?
  • 実際には instance_method というよりかは super のキャッシュのバグらしい
  • なので define_methodinstance_method を使用しない以下のコードで再現するらしい
module A
  def test(*args)
    super
  end
end

module B
  def test(a)
    puts a
  end
end

class C; end

B.prepend(A)
C.include(B)

# ここで super のシグネチャがキャッシュされている
# メソッドを上書きする前に呼ばれなければ再現しない
C.new.test 1

module B
  def test
    puts 'lambda'
  end
end

C.new.test
GitHubで編集を提案

Discussion