Patch 8.2.3364
Problem:    Vim9: crash when :for is skipped.
Solution:   Skip more code generation. (Naruhiko Nishino, closes #8777)
Files:      src/vim9compile.c, src/testdir/test_vim9_script.vim


*** ../vim-8.2.3363/src/vim9compile.c   2021-08-20 20:54:20.558119674 +0200
--- src/vim9compile.c   2021-08-21 17:25:19.035271801 +0200
***************
*** 8041,8191 ****
      }
      arg_end = arg;
  
!     // If we know the type of "var" and it is a not a supported type we can
!     // give an error now.
!     vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!     if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
!               && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY)
      {
!       semsg(_(e_for_loop_on_str_not_supported),
                                               vartype_name(vartype->tt_type));
!       drop_scope(cctx);
!       return NULL;
!     }
  
!     if (vartype->tt_type == VAR_STRING)
!       item_type = &t_string;
!     else if (vartype->tt_type == VAR_BLOB)
!       item_type = &t_number;
!     else if (vartype->tt_type == VAR_LIST
                                     && vartype->tt_member->tt_type != VAR_ANY)
!     {
!       if (!var_list)
!           item_type = vartype->tt_member;
!       else if (vartype->tt_member->tt_type == VAR_LIST
!                     && vartype->tt_member->tt_member->tt_type != VAR_ANY)
!           // TODO: should get the type for each lhs
!           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;
!     if (var_list)
!     {
!       generate_UNPACK(cctx, var_count, semicolon);
!       arg = skipwhite(arg + 1);       // skip white after '['
  
!       // the list item is replaced by a number of items
!       if (GA_GROW_FAILS(stack, var_count - 1))
        {
!           drop_scope(cctx);
!           return NULL;
!       }
!       --stack->ga_len;
!       for (idx = 0; idx < var_count; ++idx)
!       {
!           ((type_T **)stack->ga_data)[stack->ga_len] =
!                               (semicolon && idx == 0) ? vartype : item_type;
!           ++stack->ga_len;
!       }
!     }
  
!     for (idx = 0; idx < var_count; ++idx)
!     {
!       assign_dest_T   dest = dest_local;
!       int             opt_flags = 0;
!       int             vimvaridx = -1;
!       type_T          *type = &t_any;
!       type_T          *lhs_type = &t_any;
!       where_T         where = WHERE_INIT;
! 
!       p = skip_var_one(arg, FALSE);
!       varlen = p - arg;
!       name = vim_strnsave(arg, varlen);
!       if (name == NULL)
!           goto failed;
!       if (*p == ':')
!       {
!           p = skipwhite(p + 1);
!           lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
        }
  
!       // TODO: script var not supported?
!       if (get_var_dest(name, &dest, CMD_for, &opt_flags,
!                                             &vimvaridx, &type, cctx) == FAIL)
!           goto failed;
!       if (dest != dest_local)
!       {
!           if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
!                                                    0, 0, type, name) == FAIL)
!               goto failed;
!       }
!       else if (varlen == 1 && *arg == '_')
        {
!           // Assigning to "_": drop the value.
!           if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
                goto failed;
!       }
!       else
!       {
!           if (lookup_local(arg, varlen, NULL, cctx) == OK)
            {
!               semsg(_(e_variable_already_declared), arg);
!               goto failed;
            }
  
!           if (STRNCMP(name, "s:", 2) == 0)
!           {
!               semsg(_(e_cannot_declare_script_variable_in_function), name);
                goto failed;
            }
! 
!           // Reserve a variable to store "var".
!           where.wt_index = var_list ? idx + 1 : 0;
!           where.wt_variable = TRUE;
!           if (lhs_type == &t_any)
!               lhs_type = item_type;
!           else if (item_type != &t_unknown
!                       && (item_type == &t_any
!                         ? need_type(item_type, lhs_type,
                                                     -1, 0, cctx, FALSE, FALSE)
!                         : check_type(lhs_type, item_type, TRUE, where))
!                       == FAIL)
!               goto failed;
!           var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type);
!           if (var_lvar == NULL)
!               // out of memory or used as an argument
!               goto failed;
  
!           if (semicolon && idx == var_count - 1)
!               var_lvar->lv_type = vartype;
!           else
!               var_lvar->lv_type = item_type;
!           generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
        }
  
!       if (*p == ',' || *p == ';')
!           ++p;
!       arg = skipwhite(p);
!       vim_free(name);
!     }
! 
!     if (cctx->ctx_compile_type == CT_DEBUG)
!     {
!       int save_prev_lnum = cctx->ctx_prev_lnum;
  
!       // Add ISN_DEBUG here, so that the loop variables can be inspected.
!       // Use the prev_lnum from the ISN_DEBUG instruction removed above.
!       cctx->ctx_prev_lnum = prev_lnum;
!       generate_instr_debug(cctx);
!       cctx->ctx_prev_lnum = save_prev_lnum;
      }
  
      return arg_end;
--- 8041,8194 ----
      }
      arg_end = arg;
  
!     if (cctx->ctx_skip != SKIP_YES)
      {
!       // If we know the type of "var" and it is a not a supported type we can
!       // give an error now.
!       vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!       if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
!               && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY)
!       {
!           semsg(_(e_for_loop_on_str_not_supported),
                                               vartype_name(vartype->tt_type));
!           drop_scope(cctx);
!           return NULL;
!       }
  
!       if (vartype->tt_type == VAR_STRING)
!           item_type = &t_string;
!       else if (vartype->tt_type == VAR_BLOB)
!           item_type = &t_number;
!       else if (vartype->tt_type == VAR_LIST
                                     && vartype->tt_member->tt_type != VAR_ANY)
!       {
!           if (!var_list)
!               item_type = vartype->tt_member;
!           else if (vartype->tt_member->tt_type == VAR_LIST
!                         && vartype->tt_member->tt_member->tt_type != VAR_ANY)
!               // TODO: should get the type for each lhs
!               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;
!       if (var_list)
        {
!           generate_UNPACK(cctx, var_count, semicolon);
!           arg = skipwhite(arg + 1);   // skip white after '['
  
!           // the list item is replaced by a number of items
!           if (GA_GROW_FAILS(stack, var_count - 1))
!           {
!               drop_scope(cctx);
!               return NULL;
!           }
!           --stack->ga_len;
!           for (idx = 0; idx < var_count; ++idx)
!           {
!               ((type_T **)stack->ga_data)[stack->ga_len] =
!                                (semicolon && idx == 0) ? vartype : item_type;
!               ++stack->ga_len;
!           }
        }
  
!       for (idx = 0; idx < var_count; ++idx)
        {
!           assign_dest_T       dest = dest_local;
!           int         opt_flags = 0;
!           int         vimvaridx = -1;
!           type_T              *type = &t_any;
!           type_T              *lhs_type = &t_any;
!           where_T             where = WHERE_INIT;
! 
!           p = skip_var_one(arg, FALSE);
!           varlen = p - arg;
!           name = vim_strnsave(arg, varlen);
!           if (name == NULL)
                goto failed;
!           if (*p == ':')
            {
!               p = skipwhite(p + 1);
!               lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
            }
  
!           // TODO: script var not supported?
!           if (get_var_dest(name, &dest, CMD_for, &opt_flags,
!                                             &vimvaridx, &type, cctx) == FAIL)
                goto failed;
+           if (dest != dest_local)
+           {
+               if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
+                                                    0, 0, type, name) == FAIL)
+                   goto failed;
            }
!           else if (varlen == 1 && *arg == '_')
!           {
!               // Assigning to "_": drop the value.
!               if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
!                   goto failed;
!           }
!           else
!           {
!               if (lookup_local(arg, varlen, NULL, cctx) == OK)
!               {
!                   semsg(_(e_variable_already_declared), arg);
!                   goto failed;
!               }
! 
!               if (STRNCMP(name, "s:", 2) == 0)
!               {
!                   semsg(_(e_cannot_declare_script_variable_in_function), 
name);
!                   goto failed;
!               }
! 
!               // Reserve a variable to store "var".
!               where.wt_index = var_list ? idx + 1 : 0;
!               where.wt_variable = TRUE;
!               if (lhs_type == &t_any)
!                   lhs_type = item_type;
!               else if (item_type != &t_unknown
!                           && (item_type == &t_any
!                             ? need_type(item_type, lhs_type,
                                                     -1, 0, cctx, FALSE, FALSE)
!                             : check_type(lhs_type, item_type, TRUE, where))
!                           == FAIL)
!                   goto failed;
!               var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type);
!               if (var_lvar == NULL)
!                   // out of memory or used as an argument
!                   goto failed;
! 
!               if (semicolon && idx == var_count - 1)
!                   var_lvar->lv_type = vartype;
!               else
!                   var_lvar->lv_type = item_type;
!               generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
!           }
  
!           if (*p == ',' || *p == ';')
!               ++p;
!           arg = skipwhite(p);
!           vim_free(name);
        }
  
!       if (cctx->ctx_compile_type == CT_DEBUG)
!       {
!           int save_prev_lnum = cctx->ctx_prev_lnum;
  
!           // Add ISN_DEBUG here, so that the loop variables can be inspected.
!           // Use the prev_lnum from the ISN_DEBUG instruction removed above.
!           cctx->ctx_prev_lnum = prev_lnum;
!           generate_instr_debug(cctx);
!           cctx->ctx_prev_lnum = save_prev_lnum;
!       }
      }
  
      return arg_end;
***************
*** 8217,8237 ****
      }
      forscope = &scope->se_u.se_for;
      cctx->ctx_scope = scope->se_outer;
!     unwind_locals(cctx, scope->se_local_count);
  
!     // At end of ":for" scope jump back to the FOR instruction.
!     generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
  
!     // Fill in the "end" label in the FOR statement so it can jump here.
!     isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label;
!     isn->isn_arg.forloop.for_end = instr->ga_len;
  
!     // Fill in the "end" label any BREAK statements
!     compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);
  
!     // Below the ":for" scope drop the "expr" list from the stack.
!     if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
!       return NULL;
  
      vim_free(scope);
  
--- 8220,8243 ----
      }
      forscope = &scope->se_u.se_for;
      cctx->ctx_scope = scope->se_outer;
!     if (cctx->ctx_skip != SKIP_YES)
!     {
!       unwind_locals(cctx, scope->se_local_count);
  
!       // At end of ":for" scope jump back to the FOR instruction.
!       generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
  
!       // Fill in the "end" label in the FOR statement so it can jump here.
!       isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label;
!       isn->isn_arg.forloop.for_end = instr->ga_len;
  
!       // Fill in the "end" label any BREAK statements
!       compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);
  
!       // Below the ":for" scope drop the "expr" list from the stack.
!       if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
!           return NULL;
!     }
  
      vim_free(scope);
  
*** ../vim-8.2.3363/src/testdir/test_vim9_script.vim    2021-08-21 
17:13:08.569405922 +0200
--- src/testdir/test_vim9_script.vim    2021-08-21 17:20:24.836285504 +0200
***************
*** 2552,2557 ****
--- 2552,2621 ----
    delete('Xvim9for.vim')
  enddef
  
+ def Test_for_skipped_block()
+   # test skipped blocks at outside of function
+   var lines =<< trim END
+     var result = []
+     if true
+       for n in [1, 2]
+         result += [n]
+       endfor
+     else
+       for n in [3, 4]
+         result += [n]
+       endfor
+     endif
+     assert_equal([1, 2], result)
+ 
+     result = []
+     if false
+       for n in [1, 2]
+         result += [n]
+       endfor
+     else
+       for n in [3, 4]
+         result += [n]
+       endfor
+     endif
+     assert_equal([3, 4], result)
+   END
+   CheckDefAndScriptSuccess(lines)
+ 
+   # test skipped blocks at inside of function
+   lines =<< trim END
+     def DefTrue()
+       var result = []
+       if true
+         for n in [1, 2]
+           result += [n]
+         endfor
+       else
+         for n in [3, 4]
+           result += [n]
+         endfor
+       endif
+       assert_equal([1, 2], result)
+     enddef
+     DefTrue()
+ 
+     def DefFalse()
+       var result = []
+       if false
+         for n in [1, 2]
+           result += [n]
+         endfor
+       else
+         for n in [3, 4]
+           result += [n]
+         endfor
+       endif
+       assert_equal([3, 4], result)
+     enddef
+     DefFalse()
+   END
+   CheckDefAndScriptSuccess(lines)
+ enddef
+ 
  def Test_for_loop()
    var lines =<< trim END
        var result = ''
*** ../vim-8.2.3363/src/version.c       2021-08-21 17:13:08.569405922 +0200
--- src/version.c       2021-08-21 17:22:19.299865867 +0200
***************
*** 757,758 ****
--- 757,760 ----
  {   /* Add new patch number below this line */
+ /**/
+     3364,
  /**/

-- 
No children may attend school with their breath smelling of "wild onions."
                [real standing law in West Virginia, United States of America]

 /// 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/202108211527.17LFRPms1134188%40masaka.moolenaar.net.

Raspunde prin e-mail lui