Patch 8.2.2719
Problem:    Vim9: appending to dict item doesn't work in a :def function.
Solution:   Implement assignment with operator on indexed item.
Files:      src/vim9compile.c, src/testdir/test_vim9_assign.vim


*** ../vim-8.2.2718/src/vim9compile.c   2021-04-04 20:49:46.626430253 +0200
--- src/vim9compile.c   2021-04-05 16:50:46.497420275 +0200
***************
*** 2650,2655 ****
--- 2650,2761 ----
  }
  
  /*
+  * Compile getting a member from a list/dict/string/blob.  Stack has the
+  * indexable value and the index.
+  */
+     static int
+ compile_member(int is_slice, cctx_T *cctx)
+ {
+     type_T    **typep;
+     garray_T  *stack = &cctx->ctx_type_stack;
+     vartype_T vtype;
+     type_T    *valtype;
+ 
+     // We can index a list and a dict.  If we don't know the type
+     // we can use the index value type.
+     // TODO: If we don't know use an instruction to figure it out at
+     // runtime.
+     typep = ((type_T **)stack->ga_data) + stack->ga_len
+                                                 - (is_slice ? 3 : 2);
+     vtype = (*typep)->tt_type;
+     valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+     // If the index is a string, the variable must be a Dict.
+     if (*typep == &t_any && valtype == &t_string)
+       vtype = VAR_DICT;
+     if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
+     {
+       if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
+           return FAIL;
+       if (is_slice)
+       {
+           valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
+           if (need_type(valtype, &t_number, -2, 0, cctx,
+                                                        FALSE, FALSE) == FAIL)
+               return FAIL;
+       }
+     }
+ 
+     if (vtype == VAR_DICT)
+     {
+       if (is_slice)
+       {
+           emsg(_(e_cannot_slice_dictionary));
+           return FAIL;
+       }
+       if ((*typep)->tt_type == VAR_DICT)
+       {
+           *typep = (*typep)->tt_member;
+           if (*typep == &t_unknown)
+               // empty dict was used
+               *typep = &t_any;
+       }
+       else
+       {
+           if (need_type(*typep, &t_dict_any, -2, 0, cctx,
+                                                        FALSE, FALSE) == FAIL)
+               return FAIL;
+           *typep = &t_any;
+       }
+       if (may_generate_2STRING(-1, cctx) == FAIL)
+           return FAIL;
+       if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
+           return FAIL;
+     }
+     else if (vtype == VAR_STRING)
+     {
+       *typep = &t_string;
+       if ((is_slice
+               ? generate_instr_drop(cctx, ISN_STRSLICE, 2)
+               : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
+           return FAIL;
+     }
+     else if (vtype == VAR_BLOB)
+     {
+       emsg("Sorry, blob index and slice not implemented yet");
+       return FAIL;
+     }
+     else if (vtype == VAR_LIST || *typep == &t_any)
+     {
+       if (is_slice)
+       {
+           if (generate_instr_drop(cctx,
+                    vtype == VAR_LIST ?  ISN_LISTSLICE : ISN_ANYSLICE,
+                                                           2) == FAIL)
+               return FAIL;
+       }
+       else
+       {
+           if ((*typep)->tt_type == VAR_LIST)
+           {
+               *typep = (*typep)->tt_member;
+               if (*typep == &t_unknown)
+                   // empty list was used
+                   *typep = &t_any;
+           }
+           if (generate_instr_drop(cctx,
+                vtype == VAR_LIST ?  ISN_LISTINDEX : ISN_ANYINDEX, 1) == FAIL)
+               return FAIL;
+       }
+     }
+     else
+     {
+       emsg(_(e_string_list_dict_or_blob_required));
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
   * Generate an instruction to load script-local variable "name", without the
   * leading "s:".
   * Also finds imported variables.
***************
*** 3934,3943 ****
        }
        else if (**arg == '[')
        {
-           garray_T    *stack = &cctx->ctx_type_stack;
-           type_T      **typep;
-           type_T      *valtype;
-           vartype_T   vtype;
            int         is_slice = FALSE;
  
            // list index: list[123]
--- 4040,4045 ----
***************
*** 4004,4102 ****
            }
            *arg = *arg + 1;
  
!           // We can index a list and a dict.  If we don't know the type
!           // we can use the index value type.
!           // TODO: If we don't know use an instruction to figure it out at
!           // runtime.
!           typep = ((type_T **)stack->ga_data) + stack->ga_len
!                                                         - (is_slice ? 3 : 2);
!           vtype = (*typep)->tt_type;
!           valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!           // If the index is a string, the variable must be a Dict.
!           if (*typep == &t_any && valtype == &t_string)
!               vtype = VAR_DICT;
!           if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
!           {
!               if (need_type(valtype, &t_number, -1, 0, cctx,
!                                                        FALSE, FALSE) == FAIL)
!                   return FAIL;
!               if (is_slice)
!               {
!                   valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
!                   if (need_type(valtype, &t_number, -2, 0, cctx,
!                                                        FALSE, FALSE) == FAIL)
!                       return FAIL;
!               }
!           }
! 
!           if (vtype == VAR_DICT)
!           {
!               if (is_slice)
!               {
!                   emsg(_(e_cannot_slice_dictionary));
!                   return FAIL;
!               }
!               if ((*typep)->tt_type == VAR_DICT)
!               {
!                   *typep = (*typep)->tt_member;
!                   if (*typep == &t_unknown)
!                       // empty dict was used
!                       *typep = &t_any;
!               }
!               else
!               {
!                   if (need_type(*typep, &t_dict_any, -2, 0, cctx,
!                                                        FALSE, FALSE) == FAIL)
!                       return FAIL;
!                   *typep = &t_any;
!               }
!               if (may_generate_2STRING(-1, cctx) == FAIL)
!                   return FAIL;
!               if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
!                   return FAIL;
!           }
!           else if (vtype == VAR_STRING)
!           {
!               *typep = &t_string;
!               if ((is_slice
!                       ? generate_instr_drop(cctx, ISN_STRSLICE, 2)
!                       : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
!                   return FAIL;
!           }
!           else if (vtype == VAR_BLOB)
!           {
!               emsg("Sorry, blob index and slice not implemented yet");
                return FAIL;
-           }
-           else if (vtype == VAR_LIST || *typep == &t_any)
-           {
-               if (is_slice)
-               {
-                   if (generate_instr_drop(cctx,
-                            vtype == VAR_LIST ?  ISN_LISTSLICE : ISN_ANYSLICE,
-                                                                   2) == FAIL)
-                       return FAIL;
-               }
-               else
-               {
-                   if ((*typep)->tt_type == VAR_LIST)
-                   {
-                       *typep = (*typep)->tt_member;
-                       if (*typep == &t_unknown)
-                           // empty list was used
-                           *typep = &t_any;
-                   }
-                   if (generate_instr_drop(cctx,
-                            vtype == VAR_LIST ?  ISN_LISTINDEX : ISN_ANYINDEX,
-                                                                   1) == FAIL)
-                       return FAIL;
-               }
-           }
-           else
-           {
-               emsg(_(e_string_list_dict_or_blob_required));
-               return FAIL;
-           }
        }
        else if (*p == '.' && p[1] != '.')
        {
--- 4106,4113 ----
            }
            *arg = *arg + 1;
  
!           if (compile_member(is_slice, cctx) == FAIL)
                return FAIL;
        }
        else if (*p == '.' && p[1] != '.')
        {
***************
*** 5905,5913 ****
            lhs->lhs_type = lhs->lhs_lvar->lv_type;
      }
  
!     if (oplen == 3 && !heredoc && lhs->lhs_dest != dest_global
!                   && lhs->lhs_type->tt_type != VAR_STRING
!                   && lhs->lhs_type->tt_type != VAR_ANY)
      {
        emsg(_(e_can_only_concatenate_to_string));
        return FAIL;
--- 5916,5926 ----
            lhs->lhs_type = lhs->lhs_lvar->lv_type;
      }
  
!     if (oplen == 3 && !heredoc
!                  && lhs->lhs_dest != dest_global
!                  && !lhs->lhs_has_index
!                  && lhs->lhs_type->tt_type != VAR_STRING
!                  && lhs->lhs_type->tt_type != VAR_ANY)
      {
        emsg(_(e_can_only_concatenate_to_string));
        return FAIL;
***************
*** 5993,6018 ****
  }
  
  /*
!  * Assignment to a list or dict member, or ":unlet" for the item, using the
!  * information in "lhs".
!  * Returns OK or FAIL.
   */
      static int
! compile_assign_unlet(
        char_u  *var_start,
        lhs_T   *lhs,
        int     is_assign,
!       type_T  *rhs_type,
        cctx_T  *cctx)
  {
-     char_u    *p;
-     int               r;
-     vartype_T dest_type;
      size_t    varlen = lhs->lhs_varlen;
!     garray_T    *stack = &cctx->ctx_type_stack;
!     int               range = FALSE;
  
-     // Compile the "idx" in "var[idx]" or "key" in "var.key".
      p = var_start + varlen;
      if (*p == '[')
      {
--- 6006,6026 ----
  }
  
  /*
!  * For an assignment with an index, compile the "idx" in "var[idx]" or "key" 
in
!  * "var.key".
   */
      static int
! compile_assign_index(
        char_u  *var_start,
        lhs_T   *lhs,
        int     is_assign,
!       int     *range,
        cctx_T  *cctx)
  {
      size_t    varlen = lhs->lhs_varlen;
!     char_u    *p;
!     int               r = OK;
  
      p = var_start + varlen;
      if (*p == '[')
      {
***************
*** 6027,6033 ****
                semsg(_(e_cannot_use_range_with_assignment_str), p);
                return FAIL;
            }
!           range = TRUE;
            p = skipwhite(p);
            if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
            {
--- 6035,6041 ----
                semsg(_(e_cannot_use_range_with_assignment_str), p);
                return FAIL;
            }
!           *range = TRUE;
            p = skipwhite(p);
            if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
            {
***************
*** 6053,6059 ****
  
        r = generate_PUSHS(cctx, key);
      }
!     if (r == FAIL)
        return FAIL;
  
      if (lhs->lhs_type == &t_any)
--- 6061,6089 ----
  
        r = generate_PUSHS(cctx, key);
      }
!     return r;
! }
! 
! /*
!  * Assignment to a list or dict member, or ":unlet" for the item, using the
!  * information in "lhs".
!  * Returns OK or FAIL.
!  */
!     static int
! compile_assign_unlet(
!       char_u  *var_start,
!       lhs_T   *lhs,
!       int     is_assign,
!       type_T  *rhs_type,
!       cctx_T  *cctx)
! {
!     char_u    *p;
!     vartype_T dest_type;
!     size_t    varlen = lhs->lhs_varlen;
!     garray_T    *stack = &cctx->ctx_type_stack;
!     int               range = FALSE;
! 
!     if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
        return FAIL;
  
      if (lhs->lhs_type == &t_any)
***************
*** 6330,6338 ****
  
                        if (lhs.lhs_has_index)
                        {
!                           // TODO: get member from list or dict
!                           emsg("Index with operation not supported yet");
!                           goto theend;
                        }
                    }
  
--- 6360,6376 ----
  
                        if (lhs.lhs_has_index)
                        {
!                           int range = FALSE;
! 
!                           // Get member from list or dict.  First compile the
!                           // index value.
!                           if (compile_assign_index(var_start, &lhs,
!                                                  TRUE, &range, cctx) == FAIL)
!                               goto theend;
! 
!                           // Get the member.
!                           if (compile_member(FALSE, cctx) == FAIL)
!                               goto theend;
                        }
                    }
  
*** ../vim-8.2.2718/src/testdir/test_vim9_assign.vim    2021-04-03 
21:00:57.668184656 +0200
--- src/testdir/test_vim9_assign.vim    2021-04-05 17:09:23.738039270 +0200
***************
*** 1116,1122 ****
  
  def Test_assign_dict_with_op()
    var lines =<< trim END
-     vim9script
      var ds: dict<string> = {a: 'x'}
      ds['a'] ..= 'y'
      ds.a ..= 'z'
--- 1116,1121 ----
***************
*** 1148,1155 ****
      dn.a %= 6
      assert_equal(2, dn.a)
    END
!   # TODO: this should also work with a :def function
!   CheckScriptSuccess(lines)
  enddef
  
  def Test_assign_lambda()
--- 1147,1192 ----
      dn.a %= 6
      assert_equal(2, dn.a)
    END
!   CheckDefAndScriptSuccess(lines)
! enddef
! 
! def Test_assign_list_with_op()
!   var lines =<< trim END
!     var ls: list<string> = ['x']
!     ls[0] ..= 'y'
!     assert_equal('xy', ls[0])
! 
!     var ln: list<number> = [9]
!     ln[0] += 2
!     assert_equal(11, ln[0])
! 
!     ln[0] -= 3
!     assert_equal(8, ln[0])
! 
!     ln[0] *= 2
!     assert_equal(16, ln[0])
! 
!     ln[0] /= 3
!     assert_equal(5, ln[0])
! 
!     ln[0] %= 3
!     assert_equal(2, ln[0])
!   END
!   CheckDefAndScriptSuccess(lines)
! enddef
! 
! def Test_assign_with_op_fails()
!   var lines =<< trim END
!       var s = 'abc'
!       s[1] += 'x'
!   END
!   CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
! 
!   lines =<< trim END
!       var s = 'abc'
!       s[1] ..= 'x'
!   END
!   CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
  enddef
  
  def Test_assign_lambda()
*** ../vim-8.2.2718/src/version.c       2021-04-05 15:38:47.857581963 +0200
--- src/version.c       2021-04-05 15:42:34.036987624 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2719,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
62. If your doorbell rings, you think that new mail has arrived.  And then
    you're disappointed that it's only someone at the door.

 /// Bram Moolenaar -- b...@moolenaar.net -- 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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202104051511.135FBwUF3088999%40masaka.moolenaar.net.

Raspunde prin e-mail lui