Patch 8.2.3029
Problem:    Vim9: crash when using operator and list unpack assignment.
            (Naohiro Ono)
Solution:   Get variable value before operation. (closes #8416)
Files:      src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/ex_docmd.c,
            src/testdir/test_vim9_assign.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.3028/src/vim9.h  2021-06-20 19:28:10.273021391 +0200
--- src/vim9.h  2021-06-21 19:13:53.527023420 +0200
***************
*** 209,214 ****
--- 209,220 ----
      int           cuf_argcount;   // number of arguments on top of stack
  } cufunc_T;
  
+ // arguments to ISN_GETITEM
+ typedef struct {
+     varnumber_T       gi_index;
+     int               gi_with_op;
+ } getitem_T;
+ 
  typedef enum {
      JUMP_ALWAYS,
      JUMP_IF_FALSE,            // pop and jump if false
***************
*** 432,437 ****
--- 438,444 ----
        isn_T               *instr;
        tostring_T          tostring;
        tobool_T            tobool;
+       getitem_T           getitem;
      } isn_arg;
  };
  
*** ../vim-8.2.3028/src/vim9compile.c   2021-06-17 21:03:04.038634999 +0200
--- src/vim9compile.c   2021-06-21 19:11:14.315362970 +0200
***************
*** 1240,1252 ****
  
  /*
   * Generate an ISN_GETITEM instruction with "index".
   */
      static int
! generate_GETITEM(cctx_T *cctx, int index)
  {
      isn_T     *isn;
      garray_T  *stack = &cctx->ctx_type_stack;
!     type_T    *type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
      type_T    *item_type = &t_any;
  
      RETURN_OK_IF_SKIP(cctx);
--- 1240,1255 ----
  
  /*
   * Generate an ISN_GETITEM instruction with "index".
+  * "with_op" is TRUE for "+=" and other operators, the stack has the current
+  * value below the list with values.
   */
      static int
! generate_GETITEM(cctx_T *cctx, int index, int with_op)
  {
      isn_T     *isn;
      garray_T  *stack = &cctx->ctx_type_stack;
!     type_T    *type = ((type_T **)stack->ga_data)[stack->ga_len
!                                                         - (with_op ? 2 : 1)];
      type_T    *item_type = &t_any;
  
      RETURN_OK_IF_SKIP(cctx);
***************
*** 1260,1266 ****
      item_type = type->tt_member;
      if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
        return FAIL;
!     isn->isn_arg.number = index;
  
      // add the item type to the type stack
      if (ga_grow(stack, 1) == FAIL)
--- 1263,1270 ----
      item_type = type->tt_member;
      if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
        return FAIL;
!     isn->isn_arg.getitem.gi_index = index;
!     isn->isn_arg.getitem.gi_with_op = with_op;
  
      // add the item type to the type stack
      if (ga_grow(stack, 1) == FAIL)
***************
*** 6746,6764 ****
                int     is_const = FALSE;
                char_u  *wp;
  
                // For "var = expr" evaluate the expression.
                if (var_count == 0)
                {
                    int r;
  
-                   // for "+=", "*=", "..=" etc. first load the current value
-                   if (*op != '=')
-                   {
-                       if (compile_load_lhs_with_index(&lhs, var_start,
-                                                                cctx) == FAIL)
-                           goto theend;
-                   }
- 
                    // Compile the expression.
                    instr_count = instr->ga_len;
                    if (incdec)
--- 6750,6766 ----
                int     is_const = FALSE;
                char_u  *wp;
  
+               // for "+=", "*=", "..=" etc. first load the current value
+               if (*op != '='
+                       && compile_load_lhs_with_index(&lhs, var_start,
+                                                                cctx) == FAIL)
+                   goto theend;
+ 
                // For "var = expr" evaluate the expression.
                if (var_count == 0)
                {
                    int r;
  
                    // Compile the expression.
                    instr_count = instr->ga_len;
                    if (incdec)
***************
*** 6795,6801 ****
                {
                    // For "[var, var] = expr" get the "var_idx" item from the
                    // list.
!                   if (generate_GETITEM(cctx, var_idx) == FAIL)
                        goto theend;
                }
  
--- 6797,6803 ----
                {
                    // For "[var, var] = expr" get the "var_idx" item from the
                    // list.
!                   if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL)
                        goto theend;
                }
  
*** ../vim-8.2.3028/src/vim9execute.c   2021-06-20 20:09:38.590432377 +0200
--- src/vim9execute.c   2021-06-21 19:13:09.167121687 +0200
***************
*** 3832,3843 ****
            case ISN_GETITEM:
                {
                    listitem_T  *li;
!                   int         index = iptr->isn_arg.number;
  
                    // Get list item: list is at stack-1, push item.
                    // List type and length is checked for when compiling.
!                   tv = STACK_TV_BOT(-1);
!                   li = list_find(tv->vval.v_list, index);
  
                    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
                        goto theend;
--- 3832,3843 ----
            case ISN_GETITEM:
                {
                    listitem_T  *li;
!                   getitem_T   *gi = &iptr->isn_arg.getitem;
  
                    // Get list item: list is at stack-1, push item.
                    // List type and length is checked for when compiling.
!                   tv = STACK_TV_BOT(-1 - gi->gi_with_op);
!                   li = list_find(tv->vval.v_list, gi->gi_index);
  
                    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
                        goto theend;
***************
*** 3846,3852 ****
  
                    // Useful when used in unpack assignment.  Reset at
                    // ISN_DROP.
!                   ectx->ec_where.wt_index = index + 1;
                    ectx->ec_where.wt_variable = TRUE;
                }
                break;
--- 3846,3852 ----
  
                    // Useful when used in unpack assignment.  Reset at
                    // ISN_DROP.
!                   ectx->ec_where.wt_index = gi->gi_index + 1;
                    ectx->ec_where.wt_variable = TRUE;
                }
                break;
***************
*** 5376,5383 ****
            case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
            case ISN_SLICE: smsg("%s%4d SLICE %lld",
                                         pfx, current, iptr->isn_arg.number); 
break;
!           case ISN_GETITEM: smsg("%s%4d ITEM %lld",
!                                        pfx, current, iptr->isn_arg.number); 
break;
            case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
            case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
                                                  iptr->isn_arg.string); break;
--- 5376,5385 ----
            case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break;
            case ISN_SLICE: smsg("%s%4d SLICE %lld",
                                         pfx, current, iptr->isn_arg.number); 
break;
!           case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current,
!                                        iptr->isn_arg.getitem.gi_index,
!                                        iptr->isn_arg.getitem.gi_with_op ?
!                                                      " with op" : ""); break;
            case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
            case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
                                                  iptr->isn_arg.string); break;
*** ../vim-8.2.3028/src/ex_docmd.c      2021-06-19 21:38:21.728726296 +0200
--- src/ex_docmd.c      2021-06-21 19:31:00.832345242 +0200
***************
*** 3485,3490 ****
--- 3485,3492 ----
            // can't be an assignment.
            if (*eap->cmd == '[')
            {
+               char_u      *eq;
+ 
                p = to_name_const_end(eap->cmd);
                if (p == eap->cmd && *p == '[')
                {
***************
*** 3493,3504 ****
  
                    p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
                }
!               if (p == NULL || p == eap->cmd || *skipwhite(p) != '=')
                {
                    eap->cmdidx = CMD_eval;
                    return eap->cmd;
                }
!               if (p > eap->cmd && *skipwhite(p) == '=')
                {
                    eap->cmdidx = CMD_var;
                    return eap->cmd;
--- 3495,3513 ----
  
                    p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
                }
!               eq = p;
!               if (eq != NULL)
!               {
!                   eq = skipwhite(eq);
!                   if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
!                       ++eq;
!               }
!               if (p == NULL || p == eap->cmd || *eq != '=')
                {
                    eap->cmdidx = CMD_eval;
                    return eap->cmd;
                }
!               if (p > eap->cmd && *eq == '=')
                {
                    eap->cmdidx = CMD_var;
                    return eap->cmd;
*** ../vim-8.2.3028/src/testdir/test_vim9_assign.vim    2021-06-17 
21:03:04.038634999 +0200
--- src/testdir/test_vim9_assign.vim    2021-06-21 19:37:36.107229255 +0200
***************
*** 283,288 ****
--- 283,311 ----
      [v1, v2; _] = [1, 2, 3, 4, 5]
      assert_equal(1, v1)
      assert_equal(2, v2)
+ 
+     var a = 1
+     var b = 3
+     [a, b] += [2, 4]
+     assert_equal(3, a)
+     assert_equal(7, b)
+ 
+     [a, b] -= [1, 2]
+     assert_equal(2, a)
+     assert_equal(5, b)
+ 
+     [a, b] *= [3, 2]
+     assert_equal(6, a)
+     assert_equal(10, b)
+ 
+     [a, b] /= [2, 4]
+     assert_equal(3, a)
+     assert_equal(2, b)
+ 
+     [a, b] = [17, 15]
+     [a, b] %= [5, 3]
+     assert_equal(2, a)
+     assert_equal(0, b)
    END
    CheckDefAndScriptSuccess(lines)
  
*** ../vim-8.2.3028/src/testdir/test_vim9_disassemble.vim       2021-06-15 
22:13:23.829621578 +0200
--- src/testdir/test_vim9_disassemble.vim       2021-06-21 19:43:03.942335821 
+0200
***************
*** 452,457 ****
--- 452,488 ----
          res)
  enddef
  
+ def s:ListAssignWithOp()
+   var a = 2
+   var b = 3
+   [a, b] += [4, 5]
+ enddef
+ 
+ def Test_disassemble_list_assign_with_op()
+   var res = execute('disass s:ListAssignWithOp')
+   assert_match('<SNR>\d*_ListAssignWithOp\_s*' ..
+         'var a = 2\_s*' ..
+         '\d STORE 2 in $0\_s*' ..
+         'var b = 3\_s*' ..
+         '\d STORE 3 in $1\_s*' ..
+         '\[a, b\] += \[4, 5\]\_s*' ..
+         '\d\+ PUSHNR 4\_s*' ..
+         '\d\+ PUSHNR 5\_s*' ..
+         '\d\+ NEWLIST size 2\_s*' ..
+         '\d\+ CHECKLEN 2\_s*' ..
+         '\d\+ LOAD $0\_s*' ..
+         '\d\+ ITEM 0 with op\_s*' ..
+         '\d\+ OPNR +\_s*' ..
+         '\d\+ STORE $0\_s*' ..
+         '\d\+ LOAD $1\_s*' ..
+         '\d\+ ITEM 1 with op\_s*' ..
+         '\d\+ OPNR +\_s*' ..
+         '\d\+ STORE $1\_s*' ..
+         '\d\+ DROP\_s*' ..
+         '\d\+ RETURN void',
+         res)
+ enddef
+ 
  def s:ListAdd()
    var l: list<number> = []
    add(l, 123)
*** ../vim-8.2.3028/src/version.c       2021-06-21 18:43:46.136307978 +0200
--- src/version.c       2021-06-21 19:09:11.287596313 +0200
***************
*** 757,758 ****
--- 757,760 ----
  {   /* Add new patch number below this line */
+ /**/
+     3029,
  /**/

-- 
How To Keep A Healthy Level Of Insanity:
1. At lunch time, sit in your parked car with sunglasses on and point
   a hair dryer at passing cars. See if they slow down.

 /// 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/202106211744.15LHichG663941%40masaka.moolenaar.net.

Raspunde prin e-mail lui