Patch 8.2.2506
Problem:    Vim9: :continue does not work correctly in a :try block
Solution:   Add the TRYCLEANUP instruction. (closes #7827)
Files:      src/vim9compile.c, src/vim9execute.c, src/vim9.h,
            src/testdir/test_vim9_script.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.2505/src/vim9compile.c   2021-02-12 21:50:53.509801284 +0100
--- src/vim9compile.c   2021-02-13 14:11:21.597975056 +0100
***************
*** 1592,1597 ****
--- 1592,1614 ----
  
      return OK;
  }
+ /*
+  * Generate an ISN_TRYCONT instruction.
+  */
+     static int
+ generate_TRYCONT(cctx_T *cctx, int levels, int where)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr(cctx, ISN_TRYCONT)) == NULL)
+       return FAIL;
+     isn->isn_arg.trycont.tct_levels = levels;
+     isn->isn_arg.trycont.tct_where = where;
+ 
+     return OK;
+ }
+ 
  
  /*
   * Generate an ISN_BCALL instruction.
***************
*** 7314,7319 ****
--- 7331,7338 ----
  compile_continue(char_u *arg, cctx_T *cctx)
  {
      scope_T   *scope = cctx->ctx_scope;
+     int               try_scopes = 0;
+     int               loop_label;
  
      for (;;)
      {
***************
*** 7322,7336 ****
            emsg(_(e_continue));
            return NULL;
        }
!       if (scope->se_type == FOR_SCOPE || scope->se_type == WHILE_SCOPE)
            break;
        scope = scope->se_outer;
      }
  
!     // Jump back to the FOR or WHILE instruction.
!     generate_JUMP(cctx, JUMP_ALWAYS,
!           scope->se_type == FOR_SCOPE ? scope->se_u.se_for.fs_top_label
!                                         : scope->se_u.se_while.ws_top_label);
      return arg;
  }
  
--- 7341,7369 ----
            emsg(_(e_continue));
            return NULL;
        }
!       if (scope->se_type == FOR_SCOPE)
!       {
!           loop_label = scope->se_u.se_for.fs_top_label;
!           break;
!       }
!       if (scope->se_type == WHILE_SCOPE)
!       {
!           loop_label = scope->se_u.se_while.ws_top_label;
            break;
+       }
+       if (scope->se_type == TRY_SCOPE)
+           ++try_scopes;
        scope = scope->se_outer;
      }
  
!     if (try_scopes > 0)
!       // Inside one or more try/catch blocks we first need to jump to the
!       // "finally" or "endtry" to cleanup.
!       generate_TRYCONT(cctx, try_scopes, loop_label);
!     else
!       // Jump back to the FOR or WHILE instruction.
!       generate_JUMP(cctx, JUMP_ALWAYS, loop_label);
! 
      return arg;
  }
  
***************
*** 7625,7631 ****
  {
      scope_T   *scope = cctx->ctx_scope;
      garray_T  *instr = &cctx->ctx_instr;
!     isn_T     *isn;
  
      // end block scope from :catch or :finally
      if (scope != NULL && scope->se_type == BLOCK_SCOPE)
--- 7658,7664 ----
  {
      scope_T   *scope = cctx->ctx_scope;
      garray_T  *instr = &cctx->ctx_instr;
!     isn_T     *try_isn;
  
      // end block scope from :catch or :finally
      if (scope != NULL && scope->se_type == BLOCK_SCOPE)
***************
*** 7646,7656 ****
        return NULL;
      }
  
      if (cctx->ctx_skip != SKIP_YES)
      {
!       isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
!       if (isn->isn_arg.try.try_catch == 0
!                                         && isn->isn_arg.try.try_finally == 0)
        {
            emsg(_(e_missing_catch_or_finally));
            return NULL;
--- 7679,7689 ----
        return NULL;
      }
  
+     try_isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_try_label;
      if (cctx->ctx_skip != SKIP_YES)
      {
!       if (try_isn->isn_arg.try.try_catch == 0
!                                     && try_isn->isn_arg.try.try_finally == 0)
        {
            emsg(_(e_missing_catch_or_finally));
            return NULL;
***************
*** 7670,7690 ****
                                                          instr->ga_len, cctx);
  
        // End :catch or :finally scope: set value in ISN_TRY instruction
!       if (isn->isn_arg.try.try_catch == 0)
!           isn->isn_arg.try.try_catch = instr->ga_len;
!       if (isn->isn_arg.try.try_finally == 0)
!           isn->isn_arg.try.try_finally = instr->ga_len;
  
        if (scope->se_u.se_try.ts_catch_label != 0)
        {
            // Last catch without match jumps here
!           isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
            isn->isn_arg.jump.jump_where = instr->ga_len;
        }
      }
  
      compile_endblock(cctx);
  
      if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == 
NULL)
        return NULL;
  #ifdef FEAT_PROFILE
--- 7703,7729 ----
                                                          instr->ga_len, cctx);
  
        // End :catch or :finally scope: set value in ISN_TRY instruction
!       if (try_isn->isn_arg.try.try_catch == 0)
!           try_isn->isn_arg.try.try_catch = instr->ga_len;
!       if (try_isn->isn_arg.try.try_finally == 0)
!           try_isn->isn_arg.try.try_finally = instr->ga_len;
  
        if (scope->se_u.se_try.ts_catch_label != 0)
        {
            // Last catch without match jumps here
!           isn_T *isn = ((isn_T *)instr->ga_data)
!                                          + scope->se_u.se_try.ts_catch_label;
            isn->isn_arg.jump.jump_where = instr->ga_len;
        }
      }
  
      compile_endblock(cctx);
  
+     if (try_isn->isn_arg.try.try_finally == 0)
+       // No :finally encountered, use the try_finaly field to point to
+       // ENDTRY, so that TRYCONT can jump there.
+       try_isn->isn_arg.try.try_finally = cctx->ctx_instr.ga_len;
+ 
      if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == 
NULL)
        return NULL;
  #ifdef FEAT_PROFILE
***************
*** 8850,8855 ****
--- 8889,8895 ----
        case ISN_STRSLICE:
        case ISN_THROW:
        case ISN_TRY:
+       case ISN_TRYCONT:
        case ISN_UNLETINDEX:
        case ISN_UNPACK:
            // nothing allocated
*** ../vim-8.2.2505/src/vim9execute.c   2021-02-12 21:32:42.600949557 +0100
--- src/vim9execute.c   2021-02-13 14:33:03.229753249 +0100
***************
*** 27,34 ****
      int           tcd_frame_idx;      // ec_frame_idx at ISN_TRY
      int           tcd_stack_len;      // size of ectx.ec_stack at ISN_TRY
      int           tcd_catch_idx;      // instruction of the first catch
!     int           tcd_finally_idx;    // instruction of the finally block
      int           tcd_caught;         // catch block entered
      int           tcd_return;         // when TRUE return from end of :finally
  } trycmd_T;
  
--- 27,35 ----
      int           tcd_frame_idx;      // ec_frame_idx at ISN_TRY
      int           tcd_stack_len;      // size of ectx.ec_stack at ISN_TRY
      int           tcd_catch_idx;      // instruction of the first catch
!     int           tcd_finally_idx;    // instruction of the finally block or 
:endtry
      int           tcd_caught;         // catch block entered
+     int           tcd_cont;           // :continue encountered, jump here
      int           tcd_return;         // when TRUE return from end of :finally
  } trycmd_T;
  
***************
*** 2417,2423 ****
                                                        + trystack->ga_len - 1;
                    if (trycmd != NULL
                                  && trycmd->tcd_frame_idx == ectx.ec_frame_idx
!                           && trycmd->tcd_finally_idx != 0)
                    {
                        // jump to ":finally"
                        ectx.ec_iidx = trycmd->tcd_finally_idx;
--- 2418,2425 ----
                                                        + trystack->ga_len - 1;
                    if (trycmd != NULL
                                  && trycmd->tcd_frame_idx == ectx.ec_frame_idx
!                                 && ectx.ec_instr[trycmd->tcd_finally_idx]
!                                                      .isn_type != ISN_ENDTRY)
                    {
                        // jump to ":finally"
                        ectx.ec_iidx = trycmd->tcd_finally_idx;
***************
*** 2610,2615 ****
--- 2612,2645 ----
                }
                break;
  
+           case ISN_TRYCONT:
+               {
+                   garray_T    *trystack = &ectx.ec_trystack;
+                   trycont_T   *trycont = &iptr->isn_arg.trycont;
+                   int         i;
+                   trycmd_T    *trycmd;
+                   int         iidx = trycont->tct_where;
+ 
+                   if (trystack->ga_len < trycont->tct_levels)
+                   {
+                       siemsg("TRYCONT: expected %d levels, found %d",
+                                       trycont->tct_levels, trystack->ga_len);
+                       goto failed;
+                   }
+                   // Make :endtry jump to any outer try block and the last
+                   // :endtry inside the loop to the loop start.
+                   for (i = trycont->tct_levels; i > 0; --i)
+                   {
+                       trycmd = ((trycmd_T *)trystack->ga_data)
+                                                       + trystack->ga_len - i;
+                       trycmd->tcd_cont = iidx;
+                       iidx = trycmd->tcd_finally_idx;
+                   }
+                   // jump to :finally or :endtry of current try statement
+                   ectx.ec_iidx = iidx;
+               }
+               break;
+ 
            // end of ":try" block
            case ISN_ENDTRY:
                {
***************
*** 2640,2645 ****
--- 2670,2679 ----
                            --ectx.ec_stack.ga_len;
                            clear_tv(STACK_TV_BOT(0));
                        }
+                       if (trycmd->tcd_cont)
+                           // handling :continue: jump to outer try block or
+                           // start of the loop
+                           ectx.ec_iidx = trycmd->tcd_cont;
                    }
                }
                break;
***************
*** 4213,4226 ****
                {
                    try_T *try = &iptr->isn_arg.try;
  
!                   smsg("%4d TRY catch -> %d, finally -> %d", current,
!                                            try->try_catch, try->try_finally);
                }
                break;
            case ISN_CATCH:
                // TODO
                smsg("%4d CATCH", current);
                break;
            case ISN_ENDTRY:
                smsg("%4d ENDTRY", current);
                break;
--- 4247,4273 ----
                {
                    try_T *try = &iptr->isn_arg.try;
  
!                   smsg("%4d TRY catch -> %d, %s -> %d", current,
!                                try->try_catch,
!                                instr[try->try_finally].isn_type == ISN_ENDTRY
!                                                          ? "end" : "finally",
!                                try->try_finally);
                }
                break;
            case ISN_CATCH:
                // TODO
                smsg("%4d CATCH", current);
                break;
+           case ISN_TRYCONT:
+               {
+                   trycont_T *trycont = &iptr->isn_arg.trycont;
+ 
+                   smsg("%4d TRY-CONTINUE %d level%s -> %d", current,
+                                     trycont->tct_levels,
+                                     trycont->tct_levels == 1 ? "" : "s",
+                                     trycont->tct_where);
+               }
+               break;
            case ISN_ENDTRY:
                smsg("%4d ENDTRY", current);
                break;
*** ../vim-8.2.2505/src/vim9.h  2021-01-25 23:02:35.240235395 +0100
--- src/vim9.h  2021-02-13 14:11:55.721858308 +0100
***************
*** 100,105 ****
--- 100,106 ----
      ISN_PUSHEXC,    // push v:exception
      ISN_CATCH,            // drop v:exception
      ISN_ENDTRY,           // take entry off from ec_trystack
+     ISN_TRYCONT,    // handle :continue inside a :try statement
  
      // more expression operations
      ISN_ADDLIST,    // add two lists
***************
*** 209,217 ****
  // arguments to ISN_TRY
  typedef struct {
      int           try_catch;      // position to jump to on throw
!     int           try_finally;    // position to jump to for return
  } try_T;
  
  // arguments to ISN_ECHO
  typedef struct {
      int           echo_with_white;    // :echo instead of :echon
--- 210,224 ----
  // arguments to ISN_TRY
  typedef struct {
      int           try_catch;      // position to jump to on throw
!     int           try_finally;    // :finally or :endtry position to jump to
  } try_T;
  
+ // arguments to ISN_TRYCONT
+ typedef struct {
+     int           tct_levels;     // number of nested try statements
+     int           tct_where;      // position to jump to, WHILE or FOR
+ } trycont_T;
+ 
  // arguments to ISN_ECHO
  typedef struct {
      int           echo_with_white;    // :echo instead of :echon
***************
*** 333,338 ****
--- 340,346 ----
        jump_T              jump;
        forloop_T           forloop;
        try_T               try;
+       trycont_T           trycont;
        cbfunc_T            bfunc;
        cdfunc_T            dfunc;
        cpfunc_T            pfunc;
*** ../vim-8.2.2505/src/testdir/test_vim9_script.vim    2021-02-12 
21:32:42.600949557 +0100
--- src/testdir/test_vim9_script.vim    2021-02-13 15:00:48.675885815 +0100
***************
*** 2201,2206 ****
--- 2201,2223 ----
    CheckDefExecFailure(lines, 'E1017:', 1)
  enddef
  
+ def Test_for_loop_with_try_continue()
+   var looped = 0
+   var cleanup = 0
+   for i in range(3)
+     looped += 1
+     try
+       eval [][0]
+     catch
+       continue
+     finally
+       cleanup += 1
+     endtry
+   endfor
+   assert_equal(3, looped)
+   assert_equal(3, cleanup)
+ enddef
+ 
  def Test_while_loop()
    var result = ''
    var cnt = 0
*** ../vim-8.2.2505/src/testdir/test_vim9_disassemble.vim       2021-01-24 
17:53:43.681840018 +0100
--- src/testdir/test_vim9_disassemble.vim       2021-02-13 14:58:05.796429051 
+0100
***************
*** 1111,1116 ****
--- 1111,1173 ----
          instr)
  enddef
  
+ def ForLoopContinue()
+   for nr in [1, 2]
+     try
+       echo "ok"
+       try
+         echo "deeper"
+       catch
+         continue
+       endtry
+     catch
+       echo "not ok"
+     endtry
+   endfor
+ enddef
+ 
+ def Test_disassemble_for_loop_continue()
+   var instr = execute('disassemble ForLoopContinue')
+   assert_match('ForLoopContinue\_s*' ..
+         'for nr in \[1, 2]\_s*' ..
+         '0 STORE -1 in $0\_s*' ..
+         '1 PUSHNR 1\_s*' ..
+         '2 PUSHNR 2\_s*' ..
+         '3 NEWLIST size 2\_s*' ..
+         '4 FOR $0 -> 22\_s*' ..
+         '5 STORE $1\_s*' ..
+         'try\_s*' ..
+         '6 TRY catch -> 17, end -> 20\_s*' ..
+         'echo "ok"\_s*' ..
+         '7 PUSHS "ok"\_s*' ..
+         '8 ECHO 1\_s*' ..
+         'try\_s*' ..
+         '9 TRY catch -> 13, end -> 15\_s*' ..
+         'echo "deeper"\_s*' ..
+         '10 PUSHS "deeper"\_s*' ..
+         '11 ECHO 1\_s*' ..
+         'catch\_s*' ..
+         '12 JUMP -> 15\_s*' ..
+         '13 CATCH\_s*' ..
+         'continue\_s*' ..
+         '14 TRY-CONTINUE 2 levels -> 4\_s*' ..
+         'endtry\_s*' ..
+         '15 ENDTRY\_s*' ..
+         'catch\_s*' ..
+         '16 JUMP -> 20\_s*' ..
+         '17 CATCH\_s*' ..
+         'echo "not ok"\_s*' ..
+         '18 PUSHS "not ok"\_s*' ..
+         '19 ECHO 1\_s*' ..
+         'endtry\_s*' ..
+         '20 ENDTRY\_s*' ..
+         'endfor\_s*' ..
+         '21 JUMP -> 4\_s*' ..
+         '\d\+ DROP\_s*' ..
+         '\d\+ RETURN 0',
+         instr)
+ enddef
+ 
  let g:number = 42
  
  def TypeCast()
*** ../vim-8.2.2505/src/version.c       2021-02-12 22:10:18.227311434 +0100
--- src/version.c       2021-02-13 15:01:32.707739349 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2506,
  /**/

-- 
Seen on the back of a biker's vest: If you can read this, my wife fell off.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202102131403.11DE3CWm1130912%40masaka.moolenaar.net.

Raspunde prin e-mail lui