[前][次][番号順一覧][スレッド一覧]

ruby-changes:45059

From: nobu <ko1@a...>
Date: Wed, 21 Dec 2016 10:58:36 +0900 (JST)
Subject: [ruby-changes:45059] nobu:r57132 (trunk): compile.c: toplevel return

nobu	2016-12-21 10:58:32 +0900 (Wed, 21 Dec 2016)

  New Revision: 57132

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=57132

  Log:
    compile.c: toplevel return
    
    * compile.c (iseq_compile_each): stop execution of the current source
      by toplevel return.  [ruby-core:36785] [Feature #4840]

  Modified files:
    trunk/NEWS
    trunk/compile.c
    trunk/test/ruby/test_syntax.rb
Index: NEWS
===================================================================
--- NEWS	(revision 57131)
+++ NEWS	(revision 57132)
@@ -25,6 +25,8 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L25
 * Rescue modifier now applicable to method arguments.
   [Feature #12686]
 
+* Toplevel return is now allowed.  [Feature #4840]
+
 === Core classes updates (outstanding ones only)
 
 * Array
Index: test/ruby/test_syntax.rb
===================================================================
--- test/ruby/test_syntax.rb	(revision 57131)
+++ test/ruby/test_syntax.rb	(revision 57132)
@@ -926,6 +926,29 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L926
     assert_equal(:ok, result)
   end
 
+  def test_return_toplevel
+    feature4840 = '[ruby-core:36785] [Feature #4840]'
+    code = "#{<<~"begin;"}\n#{<<~"end;"}"
+    begin;
+      return; raise
+      begin return; rescue SystemExit; exit false; end
+      begin return; ensure exit false; end
+      begin ensure return; end
+      begin raise; ensure; return; end
+      begin raise; rescue; return; end
+      return false; raise
+      return 1; raise
+    end;
+    all_assertions(feature4840) do |a|
+      code.each_line do |s|
+        s.chomp!
+        a.for(s) do
+          assert_ruby_status([], s, proc {RubyVM::InstructionSequence.compile(s).disasm})
+        end
+      end
+    end
+  end
+
   private
 
   def not_label(x) @result = x; @not_label ||= nil end
Index: compile.c
===================================================================
--- compile.c	(revision 57131)
+++ compile.c	(revision 57132)
@@ -4635,12 +4635,18 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L4635
 	LABEL *lstart = NEW_LABEL(line);
 	LABEL *lend = NEW_LABEL(line);
 	LABEL *lcont = NEW_LABEL(line);
+	LINK_ELEMENT *last;
+	int last_leave = 0;
 	struct ensure_range er;
 	struct iseq_compile_data_ensure_node_stack enl;
 	struct ensure_range *erange;
 
 	INIT_ANCHOR(ensr);
 	COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr);
+	last = ensr->last;
+	last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
+	if (!popped && last_leave)
+	    popped = 1;
 
 	er.begin = lstart;
 	er.end = lend;
@@ -4657,12 +4663,15 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L4663
 	    ADD_SEQ(ret, ensr);
 	}
 	ADD_LABEL(ret, lcont);
+	if (last_leave) ADD_INSN(ret, line, pop);
 
 	erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
-	while (erange) {
-	    ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
-			    ensure, lcont);
-	    erange = erange->next;
+	if (lstart->link.next != &lend->link) {
+	    while (erange) {
+		ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
+				ensure, lcont);
+		erange = erange->next;
+	    }
 	}
 
 	ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
@@ -5463,13 +5472,20 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5472
 	rb_iseq_t *is = iseq;
 
 	if (is) {
-	    if (is->body->type == ISEQ_TYPE_TOP) {
-		COMPILE_ERROR(ERROR_ARGS "Invalid return");
+	    enum iseq_type type = is->body->type;
+	    const rb_iseq_t *parent_iseq = is->body->parent_iseq;
+	    enum iseq_type parent_type = parent_iseq ? parent_iseq->body->type : type;
+
+	    if (type == ISEQ_TYPE_TOP || type == ISEQ_TYPE_MAIN ||
+		((type == ISEQ_TYPE_RESCUE || type == ISEQ_TYPE_ENSURE) &&
+		 (parent_type == ISEQ_TYPE_TOP || parent_type == ISEQ_TYPE_MAIN))) {
+		ADD_INSN(ret, line, putnil);
+		ADD_INSN(ret, line, leave);
 	    }
 	    else {
 		LABEL *splabel = 0;
 
-		if (is->body->type == ISEQ_TYPE_METHOD) {
+		if (type == ISEQ_TYPE_METHOD) {
 		    splabel = NEW_LABEL(0);
 		    ADD_LABEL(ret, splabel);
 		    ADD_ADJUST(ret, line, 0);
@@ -5477,7 +5493,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5493
 
 		COMPILE(ret, "return nd_stts (return val)", node->nd_stts);
 
-		if (is->body->type == ISEQ_TYPE_METHOD) {
+		if (type == ISEQ_TYPE_METHOD) {
 		    add_ensure_iseq(ret, iseq, 1);
 		    ADD_TRACE(ret, line, RUBY_EVENT_RETURN);
 		    ADD_INSN(ret, line, leave);

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]