遠藤です。

[ruby-core:24794] と似たような問題は、rb_block_call でも起きるようです。


$ ./ruby -e '
class C
  include Enumerable
  alias :each :min
end
C.new.min
'
Segmentation fault


min 以外でも Enumerable のメソッドで大抵落ちるみたいです。

rb_funcall_no_recursive を真似て rb_block_call_no_recursive を作って
見ました。[ruby-dev:39592] の相互再帰の問題は同じようにあると思います。

この方針だとほぼすべての rb_block_call の呼び出しを書き換えて戻り値が
Qundef でないかどうかチェックする必要がありそうです。可読性や性能を
犠牲にしてでも、このような異常なプログラムを救済すべきでしょうか
(Qundef を返さず直接例外を投げればもう少しすっきりする?)


Index: enum.c
===================================================================
--- enum.c	(revision 25576)
+++ enum.c	(working copy)
@@ -1101,7 +1101,10 @@
 	rb_block_call(obj, id_each, 0, 0, min_ii, (VALUE)result);
     }
     else {
-	rb_block_call(obj, id_each, 0, 0, min_i, (VALUE)result);
+	VALUE ret = rb_block_call_no_recursive(obj, id_each, 0, 0, min_i,
(VALUE)result, enum_min);
+	if (ret == Qundef) {
+	    rb_raise(rb_eRuntimeError, "recursive call to Enumerable#min");
+	}
     }
     if (result[0] == Qundef) return Qnil;
     return result[0];
Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 25576)
+++ vm_eval.c	(working copy)
@@ -815,7 +815,48 @@
     return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
 }

+struct iter_method_arg_no_recursive {
+    struct iter_method_arg arg;
+    VALUE (*func)();
+};
+
+static VALUE
+iterate_method_no_recursive(VALUE obj)
+{
+    const struct iter_method_arg_no_recursive * arg =
+      (struct iter_method_arg_no_recursive *) obj;
+    rb_method_entry_t *me = rb_search_method_emtry(arg->arg.obj, arg->arg.mid);
+    rb_thread_t *th = GET_THREAD();
+    int call_status;
+
+    if (!me) return Qundef;
+    if (me->def && me->def->type == VM_METHOD_TYPE_CFUNC &&
+	me->def->body.cfunc.func == arg->func)
+	return Qundef;
+    call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef);
+    if (call_status != NOEX_OK) {
+	return Qundef;
+    }
+    stack_check();
+    iterate_method((VALUE) &arg->arg);
+}
+
 VALUE
+rb_block_call_no_recursive(VALUE obj, ID mid, int argc, VALUE * argv,
+			   VALUE (*bl_proc) (ANYARGS), VALUE data2,
+			   VALUE (*func)())
+{
+    struct iter_method_arg_no_recursive arg;
+
+    arg.arg.obj = obj;
+    arg.arg.mid = mid;
+    arg.arg.argc = argc;
+    arg.arg.argv = argv;
+    arg.func = func;
+    return rb_iterate(iterate_method_no_recursive, (VALUE)&arg,
bl_proc, data2);
+}
+
+VALUE
 rb_each(VALUE obj)
 {
     return rb_call(obj, idEach, 0, 0, CALL_FCALL);

-- 
Yusuke ENDOH <mame / tsg.ne.jp>