summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--NEWS5
-rw-r--r--test/ruby/test_enumerator.rb8
-rw-r--r--vm_eval.c25
4 files changed, 40 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 61dd12bafd..71e940de2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Thu Oct 22 18:52:53 2015 Akinori MUSHA <knu@iDaemons.org>
+
+ * vm_eval.c (rb_f_loop): When a loop is stopped by a StopIteration
+ exception, return what the enumerator has returned instead of
+ nil. [ruby-core:71133] [Feature #11498]
+
Thu Oct 22 18:25:10 2015 Shugo Maeda <shugo@ruby-lang.org>
* lib/net/imap (idle): add a new argument timeout for keep-alive.
diff --git a/NEWS b/NEWS
index 8a2c7f5846..ee347083a6 100644
--- a/NEWS
+++ b/NEWS
@@ -64,6 +64,11 @@ with all sufficient information, see the ChangeLog file.
this parameter is bitwise-ORed to oflags generated by normal mode argument.
[Feature #11253]
+* Kernel
+
+ * Kernel#loop, when stopped by a StopIteration exception, returns
+ what the enumerator has returned instead of nil.
+
* Module
* Module#deprecate_constant [Feature #11398]
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 91c91a4bbd..e81d2e4e72 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -46,6 +46,14 @@ class TestEnumerator < Test::Unit::TestCase
}
end
+ def test_loop_return_value
+ assert_equal nil, loop { break }
+ assert_equal 42, loop { break 42 }
+
+ e = Enumerator.new { |y| y << 1; y << 2; :stopped }
+ assert_equal :stopped, loop { e.next while true }
+ end
+
def test_nested_iteration
def (o = Object.new).each
yield :ok1
diff --git a/vm_eval.c b/vm_eval.c
index 780a83bba1..121e2f15bd 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -24,7 +24,7 @@ static void vm_set_eval_stack(rb_thread_t * th, const rb_iseq_t *iseq, const rb_
static int vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *dfp, const struct local_var_list *vars);
static VALUE rb_eUncaughtThrow;
-static ID id_tag, id_value;
+static ID id_result, id_tag, id_value;
#define id_mesg idMesg
/* vm_backtrace.c */
@@ -1079,6 +1079,12 @@ loop_i(void)
}
static VALUE
+loop_stop(VALUE dummy, VALUE exc)
+{
+ return rb_attr_get(exc, id_result);
+}
+
+static VALUE
rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
{
return DBL2NUM(INFINITY);
@@ -1100,15 +1106,25 @@ rb_f_loop_size(VALUE self, VALUE args, VALUE eobj)
* # ...
* end
*
- * StopIteration raised in the block breaks the loop.
+ * StopIteration raised in the block breaks the loop. In this case,
+ * loop returns the "result" value stored in the exception.
+ *
+ * enum = Enumerator.new { |y|
+ * y << "one"
+ * y << "two"
+ * :ok
+ * }
+ *
+ * result = loop {
+ * puts enum.next
+ * } #=> :ok
*/
static VALUE
rb_f_loop(VALUE self)
{
RETURN_SIZED_ENUMERATOR(self, 0, 0, rb_f_loop_size);
- rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0);
- return Qnil; /* dummy */
+ return rb_rescue2(loop_i, (VALUE)0, loop_stop, (VALUE)0, rb_eStopIteration, (VALUE)0);
}
#if VMDEBUG
@@ -2184,6 +2200,7 @@ Init_vm_eval(void)
rb_define_method(rb_eUncaughtThrow, "value", uncaught_throw_value, 0);
rb_define_method(rb_eUncaughtThrow, "to_s", uncaught_throw_to_s, 0);
+ id_result = rb_intern_const("result");
id_tag = rb_intern_const("tag");
id_value = rb_intern_const("value");
}