Patch 8.2.2651
Problem:    Vim9: restoring command modifiers happens after jump.
Solution:   Move the restore instruction to before the jump. (closes #8006)
            Also handle for and while.
Files:      src/vim9compile.c, src/vim9execute.c,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.2650/src/vim9compile.c   2021-03-21 22:12:31.448826619 +0100
--- src/vim9compile.c   2021-03-25 20:58:01.536755646 +0100
***************
*** 2172,2177 ****
--- 2172,2216 ----
      return OK;
  }
  
+ /*
+  * If an ISN_CMDMOD was just generated drop it.
+  */
+     static void
+ drop_cmdmod(cctx_T *cctx)
+ {
+     garray_T  *instr = &cctx->ctx_instr;
+ 
+     // Drop any CMDMOD instruction
+     if (cctx->ctx_has_cmdmod
+           && ((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type
+                                                                == ISN_CMDMOD)
+     {
+       --instr->ga_len;
+       cctx->ctx_has_cmdmod = FALSE;
+     }
+ }
+ 
+ /*
+  * Get the index of the current instruction.
+  * This compenstates for a preceding ISN_CMDMOD and ISN_PROF_START.
+  */
+     static int
+ current_instr_idx(cctx_T *cctx)
+ {
+     garray_T  *instr = &cctx->ctx_instr;
+     int               idx = instr->ga_len;
+ 
+     if (cctx->ctx_has_cmdmod && ((isn_T *)instr->ga_data)[idx - 1]
+                                                      .isn_type == ISN_CMDMOD)
+       --idx;
+ #ifdef FEAT_PROFILE
+     if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[idx - 1]
+                                                  .isn_type == ISN_PROF_START)
+       --idx;
+ #endif
+     return idx;
+ }
+ 
  #ifdef FEAT_PROFILE
      static void
  may_generate_prof_end(cctx_T *cctx, int prof_lnum)
***************
*** 6877,6882 ****
--- 6916,6924 ----
            return NULL;
      }
  
+     // CMDMOD_REV must come before the jump
+     generate_undo_cmdmods(cctx);
+ 
      scope = new_scope(cctx, IF_SCOPE);
      if (scope == NULL)
        return NULL;
***************
*** 6937,6960 ****
      if (scope->se_u.se_if.is_seen_skip_not)
      {
        // A previous block was executed, skip over expression and bail out.
!       // Do not count the "elseif" for profiling.
! #ifdef FEAT_PROFILE
!       if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
!                                                  .isn_type == ISN_PROF_START)
!           --instr->ga_len;
! #endif
        skip_expr_cctx(&p, cctx);
        return p;
      }
  
      if (cctx->ctx_skip == SKIP_UNKNOWN)
      {
        if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
                                                    JUMP_ALWAYS, cctx) == FAIL)
            return NULL;
        // previous "if" or "elseif" jumps here
        isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
        isn->isn_arg.jump.jump_where = instr->ga_len;
      }
  
      // compile "expr"; if we know it evaluates to FALSE skip the block
--- 6979,7014 ----
      if (scope->se_u.se_if.is_seen_skip_not)
      {
        // A previous block was executed, skip over expression and bail out.
!       // Do not count the "elseif" for profiling and cmdmod
!       instr->ga_len = current_instr_idx(cctx);
! 
        skip_expr_cctx(&p, cctx);
        return p;
      }
  
      if (cctx->ctx_skip == SKIP_UNKNOWN)
      {
+       int moved_cmdmod = FALSE;
+ 
+       // Move any CMDMOD instruction to after the jump
+       if (((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type == ISN_CMDMOD)
+       {
+           if (ga_grow(instr, 1) == FAIL)
+               return NULL;
+           ((isn_T *)instr->ga_data)[instr->ga_len] =
+                                 ((isn_T *)instr->ga_data)[instr->ga_len - 1];
+           --instr->ga_len;
+           moved_cmdmod = TRUE;
+       }
+ 
        if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
                                                    JUMP_ALWAYS, cctx) == FAIL)
            return NULL;
        // previous "if" or "elseif" jumps here
        isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
        isn->isn_arg.jump.jump_where = instr->ga_len;
+       if (moved_cmdmod)
+           ++instr->ga_len;
      }
  
      // compile "expr"; if we know it evaluates to FALSE skip the block
***************
*** 7007,7012 ****
--- 7061,7069 ----
        if (bool_on_stack(cctx) == FAIL)
            return NULL;
  
+       // CMDMOD_REV must come before the jump
+       generate_undo_cmdmods(cctx);
+ 
        // "where" is set when ":elseif", "else" or ":endif" is found
        scope->se_u.se_if.is_if_label = instr->ga_len;
        generate_JUMP(cctx, JUMP_IF_FALSE, 0);
***************
*** 7090,7095 ****
--- 7147,7153 ----
      garray_T  *instr = &cctx->ctx_instr;
      isn_T     *isn;
  
+     drop_cmdmod(cctx);
      if (scope == NULL || scope->se_type != IF_SCOPE)
      {
        emsg(_(e_endif_without_if));
***************
*** 7160,7166 ****
      int               var_count = 0;
      int               semicolon = FALSE;
      size_t    varlen;
-     garray_T  *instr = &cctx->ctx_instr;
      garray_T  *stack = &cctx->ctx_type_stack;
      scope_T   *scope;
      lvar_T    *loop_lvar;     // loop iteration variable
--- 7218,7223 ----
***************
*** 7230,7237 ****
            item_type = vartype->tt_member->tt_member;
      }
  
      // "for_end" is set when ":endfor" is found
!     scope->se_u.se_for.fs_top_label = instr->ga_len;
      generate_FOR(cctx, loop_lvar->lv_idx);
  
      arg = arg_start;
--- 7287,7297 ----
            item_type = vartype->tt_member->tt_member;
      }
  
+     // CMDMOD_REV must come before the FOR instruction
+     generate_undo_cmdmods(cctx);
+ 
      // "for_end" is set when ":endfor" is found
!     scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
      generate_FOR(cctx, loop_lvar->lv_idx);
  
      arg = arg_start;
***************
*** 7333,7338 ****
--- 7393,7400 ----
      forscope_T        *forscope;
      isn_T     *isn;
  
+     drop_cmdmod(cctx);
+ 
      if (scope == NULL || scope->se_type != FOR_SCOPE)
      {
        emsg(_(e_for));
***************
*** 7376,7395 ****
  compile_while(char_u *arg, cctx_T *cctx)
  {
      char_u    *p = arg;
-     garray_T  *instr = &cctx->ctx_instr;
      scope_T   *scope;
  
      scope = new_scope(cctx, WHILE_SCOPE);
      if (scope == NULL)
        return NULL;
  
!     // "endwhile" jumps back here, one before when profiling
!     scope->se_u.se_while.ws_top_label = instr->ga_len;
! #ifdef FEAT_PROFILE
!     if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
!                                                  .isn_type == ISN_PROF_START)
!       --scope->se_u.se_while.ws_top_label;
! #endif
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
--- 7438,7451 ----
  compile_while(char_u *arg, cctx_T *cctx)
  {
      char_u    *p = arg;
      scope_T   *scope;
  
      scope = new_scope(cctx, WHILE_SCOPE);
      if (scope == NULL)
        return NULL;
  
!     // "endwhile" jumps back here, one before when profiling or using cmdmods
!     scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
  
      // compile "expr"
      if (compile_expr0(&p, cctx) == FAIL)
***************
*** 7403,7408 ****
--- 7459,7467 ----
      if (bool_on_stack(cctx) == FAIL)
        return FAIL;
  
+     // CMDMOD_REV must come before the jump
+     generate_undo_cmdmods(cctx);
+ 
      // "while_end" is set when ":endwhile" is found
      if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
                                                  JUMP_IF_FALSE, cctx) == FAIL)
***************
*** 7420,7425 ****
--- 7479,7485 ----
      scope_T   *scope = cctx->ctx_scope;
      garray_T  *instr = &cctx->ctx_instr;
  
+     drop_cmdmod(cctx);
      if (scope == NULL || scope->se_type != WHILE_SCOPE)
      {
        emsg(_(e_while));
*** ../vim-8.2.2650/src/vim9execute.c   2021-03-24 22:00:52.042056113 +0100
--- src/vim9execute.c   2021-03-25 20:32:43.784445430 +0100
***************
*** 796,801 ****
--- 796,816 ----
  }
  
  /*
+  * If command modifiers were applied restore them.
+  */
+     static void
+ may_restore_cmdmod(funclocal_T *funclocal)
+ {
+     if (funclocal->floc_restore_cmdmod)
+     {
+       cmdmod.cmod_filter_regmatch.regprog = NULL;
+       undo_cmdmod(&cmdmod);
+       cmdmod = funclocal->floc_save_cmdmod;
+       funclocal->floc_restore_cmdmod = FALSE;
+     }
+ }
+ 
+ /*
   * Return TRUE if an error was given or CTRL-C was pressed.
   */
      static int
***************
*** 2719,2726 ****
--- 2734,2744 ----
                        goto failed;
                    ++idxtv->vval.v_number;
                    if (list == NULL || idxtv->vval.v_number >= list->lv_len)
+                   {
                        // past the end of the list, jump to "endfor"
                        ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
+                       may_restore_cmdmod(&funclocal);
+                   }
                    else if (list->lv_first == &range_list_item)
                    {
                        // non-materialized range() list
***************
*** 2755,2763 ****
                    CLEAR_POINTER(trycmd);
                    trycmd->tcd_frame_idx = ectx.ec_frame_idx;
                    trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
!                   trycmd->tcd_catch_idx = 
iptr->isn_arg.try.try_ref->try_catch;
!                   trycmd->tcd_finally_idx = 
iptr->isn_arg.try.try_ref->try_finally;
!                   trycmd->tcd_endtry_idx = 
iptr->isn_arg.try.try_ref->try_endtry;
                }
                break;
  
--- 2773,2784 ----
                    CLEAR_POINTER(trycmd);
                    trycmd->tcd_frame_idx = ectx.ec_frame_idx;
                    trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
!                   trycmd->tcd_catch_idx =
!                                         iptr->isn_arg.try.try_ref->try_catch;
!                   trycmd->tcd_finally_idx =
!                                       iptr->isn_arg.try.try_ref->try_finally;
!                   trycmd->tcd_endtry_idx =
!                                        iptr->isn_arg.try.try_ref->try_endtry;
                }
                break;
  
***************
*** 2782,2794 ****
                {
                    garray_T    *trystack = &ectx.ec_trystack;
  
!                   if (funclocal.floc_restore_cmdmod)
!                   {
!                       cmdmod.cmod_filter_regmatch.regprog = NULL;
!                       undo_cmdmod(&cmdmod);
!                       cmdmod = funclocal.floc_save_cmdmod;
!                       funclocal.floc_restore_cmdmod = FALSE;
!                   }
                    if (trystack->ga_len > 0)
                    {
                        trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
--- 2803,2809 ----
                {
                    garray_T    *trystack = &ectx.ec_trystack;
  
!                   may_restore_cmdmod(&funclocal);
                    if (trystack->ga_len > 0)
                    {
                        trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
*** ../vim-8.2.2650/src/testdir/test_vim9_disassemble.vim       2021-03-17 
18:42:04.442869185 +0100
--- src/testdir/test_vim9_disassemble.vim       2021-03-25 20:57:15.376870119 
+0100
***************
*** 1896,1902 ****
          '\d PUSHS "error"\_s*' ..
          '\d ECHOERR 1\_s*' ..
          '\d CMDMOD_REV\_s*' ..
!         '\d RETURN 0',
          res)
  enddef
  
--- 1896,1990 ----
          '\d PUSHS "error"\_s*' ..
          '\d ECHOERR 1\_s*' ..
          '\d CMDMOD_REV\_s*' ..
!         '\d\+ RETURN 0',
!         res)
! enddef
! 
! def s:SilentIf()
!   silent if 4 == g:five
!   silent elseif 4 == g:five
!   silent endif
! enddef
! 
! def Test_silent_if()
!   var res = execute('disass s:SilentIf')
!   assert_match('<SNR>\d*_SilentIf\_s*' ..
!         'silent if 4 == g:five\_s*' ..
!         '\d\+ CMDMOD silent\_s*' ..
!         '\d\+ PUSHNR 4\_s*' ..
!         '\d\+ LOADG g:five\_s*' ..
!         '\d\+ COMPAREANY ==\_s*' ..
!         '\d\+ CMDMOD_REV\_s*' ..
!         '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
!         'silent elseif 4 == g:five\_s*' ..
!         '\d\+ JUMP -> \d\+\_s*' ..
!         '\d\+ CMDMOD silent\_s*' ..
!         '\d\+ PUSHNR 4\_s*' ..
!         '\d\+ LOADG g:five\_s*' ..
!         '\d\+ COMPAREANY ==\_s*' ..
!         '\d\+ CMDMOD_REV\_s*' ..
!         '\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
!         'silent endif\_s*' ..
!         '\d\+ RETURN 0',
!         res)
! enddef
! 
! def s:SilentFor()
!   silent for i in [0]
!   silent endfor
! enddef
! 
! def Test_silent_for()
!   var res = execute('disass s:SilentFor')
!   assert_match('<SNR>\d*_SilentFor\_s*' ..
!         'silent for i in \[0\]\_s*' ..
!         '\d CMDMOD silent\_s*' ..
!         '\d STORE -1 in $0\_s*' ..
!         '\d PUSHNR 0\_s*' ..
!         '\d NEWLIST size 1\_s*' ..
!         '\d CMDMOD_REV\_s*' ..
!         '5 FOR $0 -> 8\_s*' ..
!         '\d STORE $1\_s*' ..
!         'silent endfor\_s*' ..
!         '\d JUMP -> 5\_s*' ..
!         '8 DROP\_s*' ..
!         '\d RETURN 0\_s*',
!         res)
! enddef
! 
! def s:SilentWhile()
!   silent while g:not
!   silent endwhile
! enddef
! 
! def Test_silent_while()
!   var res = execute('disass s:SilentWhile')
!   assert_match('<SNR>\d*_SilentWhile\_s*' ..
!         'silent while g:not\_s*' ..
!         '0 CMDMOD silent\_s*' ..
!         '\d LOADG g:not\_s*' ..
!         '\d COND2BOOL\_s*' ..
!         '\d CMDMOD_REV\_s*' ..
!         '\d JUMP_IF_FALSE -> 6\_s*' ..
! 
!         'silent endwhile\_s*' ..
!         '\d JUMP -> 0\_s*' ..
!         '6 RETURN 0\_s*',
!          res)
! enddef
! 
! def s:SilentReturn(): string
!   silent return "done"
! enddef
! 
! def Test_silent_return()
!   var res = execute('disass s:SilentReturn')
!   assert_match('<SNR>\d*_SilentReturn\_s*' ..
!         'silent return "done"\_s*' ..
!         '\d CMDMOD silent\_s*' ..
!         '\d PUSHS "done"\_s*' ..
!         '\d CMDMOD_REV\_s*' ..
!         '\d RETURN',
          res)
  enddef
  
***************
*** 1924,1942 ****
          res)
  enddef
  
- def s:SilentReturn(): string
-   silent return "done"
- enddef
- 
- def Test_silent_return()
-   var res = execute('disass s:SilentReturn')
-   assert_match('<SNR>\d*_SilentReturn\_s*' ..
-         'silent return "done"\_s*' ..
-         '\d CMDMOD silent\_s*' ..
-         '\d PUSHS "done"\_s*' ..
-         '\d CMDMOD_REV\_s*' ..
-         '\d RETURN',
-         res)
- enddef
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- 2012,2016 ----
*** ../vim-8.2.2650/src/version.c       2021-03-24 22:00:52.046056095 +0100
--- src/version.c       2021-03-25 19:35:19.530039581 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2651,
  /**/

-- 
How To Keep A Healthy Level Of Insanity:
4. Put your garbage can on your desk and label it "in".

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/202103252013.12PKDFi3139044%40masaka.moolenaar.net.

Raspunde prin e-mail lui