Bug #2657

rubyspec: The return keyword within define_method goes through the method via a closure FAILED

Added by Yusuke Endoh about 5 years ago. Updated almost 4 years ago.

[ruby-dev:40158]
Status:Closed
Priority:Normal
Assignee:Koichi Sasada
ruby -v:ruby 1.9.2dev (2010-01-26 trunk 26420) [i686-linux] Backport:

Description

=begin
ささださん
遠藤です。

rubyspec の以下のエラーを調べてみました。

9)
The return keyword within define_method goes through the method via a closure FAILED
Expected :bad
to equal :good

/home/mame/work/ruby/spec/rubyspec/language/return_spec.rb:269:in block (3 levels) in <top (required)>'
/home/mame/work/ruby/spec/rubyspec/language/return_spec.rb:4:in
'

○問題

$ ./ruby -e '
class C
define_method(:foo) do |&x|
x.call
end
def outer
foo { return :good }
return :bad
end
end
p C.new.outer
'
ok
:bad

というプログラムが、:good でなく :bad を表示してしまいます。ついでに
謎の ok も出ています。

1.8 では :good が出ますし、define_method を def に置き換えると 1.9 でも
:good が出ますので、:good と出るのが正しそうです。

○原因

rb_vm_invoke_proc の中で lambda の中の TAG_RETURN を止めてしまうことが
原因です。

この処理は一見すると、「lambda 中の return が lambda で止まるために
必要」と思ってしまいますが、TAG_RETURN は普通に vm_exec の中で処理され
goto finish_vme; によって止められますので、不要だと思われます。

この処理が不要であることの状況証拠として、

  • この処理を消してもテストのエラーは増えない (上記の rubyspec の分減る)

  • カバレッジを見ても、上記の rubyspec のテスト以外では実行されていない
    (http://dame.dyndns.org:7001/20100126/ruby/vm.c の rb_vm_invoke_proc
    の中のこのチェックのカバレッジが 1 回だけ)

  • この処理にはデバッグ用の printf 文が残っている (2008-06-17 の r17390
    からずっと) のに、普通に lambda { return }.call などしても出会わない

というわけで、当時は必要だったのかもしれませんが、少なくとも現在では
この処理は害でしかないと思います。

○パッチ

問題の処理を消します。

diff --git a/vm.c b/vm.c
index bb19ecb..f03cf31 100644
--- a/vm.c
+++ b/vm.c
@@ -609,21 +609,6 @@ rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
}

  if (state) {
  • if (state == TAG_RETURN && proc->is_lambda) {
  • VALUE err = th->errinfo;
  • VALUE *escape_dfp = GET_THROWOBJ_CATCH_POINT(err); -
  • if (escape_dfp == cfp->dfp) {
  • printf("ok\n");
  • state = 0;
  • th->errinfo = Qnil;
  • th->cfp = cfp;
  • val = GET_THROWOBJ_VAL(err);
  • }
  • }
  • } -
  • if (state) { JUMP_TAG(state); } return val;

--
Yusuke ENDOH mame@tsg.ne.jp
=end

History

#1 Updated by Koichi Sasada about 5 years ago

=begin
 ささだです.

(2010/01/26 19:59), Yusuke Endoh wrote::

○原因

rb_vm_invoke_proc の中で lambda の中の TAG_RETURN を止めてしまうことが
原因です。

この処理は一見すると、「lambda 中の return が lambda で止まるために
必要」と思ってしまいますが、TAG_RETURN は普通に vm_exec の中で処理され
goto finish_vme; によって止められますので、不要だと思われます。

この処理が不要であることの状況証拠として、

  • この処理を消してもテストのエラーは増えない (上記の rubyspec の分減る)

  • カバレッジを見ても、上記の rubyspec のテスト以外では実行されていない
    (http://dame.dyndns.org:7001/20100126/ruby/vm.c の rb_vm_invoke_proc
    の中のこのチェックのカバレッジが 1 回だけ)

  • この処理にはデバッグ用の printf 文が残っている (2008-06-17 の r17390
    からずっと) のに、普通に lambda { return }.call などしても出会わない

というわけで、当時は必要だったのかもしれませんが、少なくとも現在では
この処理は害でしかないと思います。

 はい.仰る通りかと思います.修正して下さいませ.

--
// SASADA Koichi at atdot dot net
=end

#2 Updated by Koichi Sasada about 5 years ago

=begin
 ささだです.

(2010/01/26 19:59), Yusuke Endoh wrote::

○原因

rb_vm_invoke_proc の中で lambda の中の TAG_RETURN を止めてしまうことが
原因です。

この処理は一見すると、「lambda 中の return が lambda で止まるために
必要」と思ってしまいますが、TAG_RETURN は普通に vm_exec の中で処理され
goto finish_vme; によって止められますので、不要だと思われます。

この処理が不要であることの状況証拠として、

  • この処理を消してもテストのエラーは増えない (上記の rubyspec の分減る)

  • カバレッジを見ても、上記の rubyspec のテスト以外では実行されていない
    (http://dame.dyndns.org:7001/20100126/ruby/vm.c の rb_vm_invoke_proc
    の中のこのチェックのカバレッジが 1 回だけ)

  • この処理にはデバッグ用の printf 文が残っている (2008-06-17 の r17390
    からずっと) のに、普通に lambda { return }.call などしても出会わない

というわけで、当時は必要だったのかもしれませんが、少なくとも現在では
この処理は害でしかないと思います。

 はい.仰る通りかと思います.修正して下さいませ.

--
// SASADA Koichi at atdot dot net

=end

#3 Updated by Yusuke Endoh about 5 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
This issue was solved with changeset r26425.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Also available in: Atom PDF