Patch 8.2.3332
Problem:    Vim9: cannot assign to range in list.
Solution:   Implement overwriting a list range.
Files:      src/vim9compile.c, src/vim9execute.c, src/list.c,
            src/proto/list.pro, src/eval.c, src/proto/eval.pro,
            src/testdir/test_listdict.vim, src/testdir/test_vim9_assign.vim


*** ../vim-8.2.3331/src/vim9compile.c   2021-08-10 22:51:59.369449616 +0200
--- src/vim9compile.c   2021-08-11 18:20:36.658036480 +0200
***************
*** 6485,6490 ****
--- 6485,6513 ----
  }
  
  /*
+  * Return TRUE if "lhs" has a range index: "[expr : expr]".
+  */
+     static int
+ has_list_index(char_u *idx_start, cctx_T *cctx)
+ {
+     char_u  *p = idx_start;
+     int           save_skip;
+ 
+     if (*p != '[')
+       return FALSE;
+ 
+     p = skipwhite(p + 1);
+     if (*p == ':')
+       return TRUE;
+ 
+     save_skip = cctx->ctx_skip;
+     cctx->ctx_skip = SKIP_YES;
+     (void)compile_expr0(&p, cctx);
+     cctx->ctx_skip = save_skip;
+     return *skipwhite(p) == ':';
+ }
+ 
+ /*
   * For an assignment with an index, compile the "idx" in "var[idx]" or "key" 
in
   * "var.key".
   */
***************
*** 6652,6659 ****
  
      if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL)
        return FAIL;
!     if (is_assign && range && lhs->lhs_type != &t_blob
!                                                   && lhs->lhs_type != &t_any)
      {
        semsg(_(e_cannot_use_range_with_assignment_str), var_start);
        return FAIL;
--- 6675,6684 ----
  
      if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL)
        return FAIL;
!     if (is_assign && range
!           && lhs->lhs_type->tt_type != VAR_LIST
!           && lhs->lhs_type != &t_blob
!           && lhs->lhs_type != &t_any)
      {
        semsg(_(e_cannot_use_range_with_assignment_str), var_start);
        return FAIL;
***************
*** 7029,7035 ****
                        SOURCING_LNUM = start_lnum;
                        where.wt_index = var_count > 0 ? var_idx + 1 : 0;
                        where.wt_variable = var_count > 0;
!                       if (lhs.lhs_has_index)
                            use_type = lhs.lhs_member_type;
                        if (need_type_where(rhs_type, use_type, -1, where,
                                    cctx, FALSE, is_const) == FAIL)
--- 7054,7064 ----
                        SOURCING_LNUM = start_lnum;
                        where.wt_index = var_count > 0 ? var_idx + 1 : 0;
                        where.wt_variable = var_count > 0;
!                       // If assigning to a list or dict member, use the
!                       // member type.  Not for "list[:] =".
!                       if (lhs.lhs_has_index
!                               && !has_list_index(var_start + lhs.lhs_varlen,
!                                                                        cctx))
                            use_type = lhs.lhs_member_type;
                        if (need_type_where(rhs_type, use_type, -1, where,
                                    cctx, FALSE, is_const) == FAIL)
*** ../vim-8.2.3331/src/vim9execute.c   2021-08-07 15:50:20.179575314 +0200
--- src/vim9execute.c   2021-08-11 21:41:37.372657009 +0200
***************
*** 2503,2516 ****
                    // -4 value to be stored
                    // -3 first index or "none"
                    // -2 second index or "none"
!                   // -1 destination blob
                    tv = STACK_TV_BOT(-4);
!                   if (tv_dest->v_type != VAR_BLOB)
                    {
!                       status = FAIL;
!                       emsg(_(e_blob_required));
                    }
!                   else
                    {
                        varnumber_T n1;
                        varnumber_T n2;
--- 2503,2555 ----
                    // -4 value to be stored
                    // -3 first index or "none"
                    // -2 second index or "none"
!                   // -1 destination list or blob
                    tv = STACK_TV_BOT(-4);
!                   if (tv_dest->v_type == VAR_LIST)
                    {
!                       long    n1;
!                       long    n2;
!                       int     error = FALSE;
! 
!                       SOURCING_LNUM = iptr->isn_lnum;
!                       n1 = (long)tv_get_number_chk(tv_idx1, &error);
!                       if (error)
!                           status = FAIL;
!                       else
!                       {
!                           if (tv_idx2->v_type == VAR_SPECIAL
!                                       && tv_idx2->vval.v_number == VVAL_NONE)
!                               n2 = list_len(tv_dest->vval.v_list) - 1;
!                           else
!                               n2 = (long)tv_get_number_chk(tv_idx2, &error);
!                           if (error)
!                               status = FAIL;
!                           else
!                           {
!                               listitem_T *li1 = check_range_index_one(
!                                       tv_dest->vval.v_list, &n1, FALSE);
! 
!                               if (li1 == NULL)
!                                   status = FAIL;
!                               else
!                               {
!                                   status = check_range_index_two(
!                                           tv_dest->vval.v_list,
!                                           &n1, li1, &n2, FALSE);
!                                   if (status != FAIL)
!                                       status = list_assign_range(
!                                               tv_dest->vval.v_list,
!                                               tv->vval.v_list,
!                                               n1,
!                                               n2,
!                                               tv_idx2->v_type == VAR_SPECIAL,
!                                               (char_u *)"=",
!                                               (char_u *)"[unknown]");
!                               }
!                           }
!                       }
                    }
!                   else if (tv_dest->v_type == VAR_BLOB)
                    {
                        varnumber_T n1;
                        varnumber_T n2;
***************
*** 2530,2536 ****
                                status = FAIL;
                            else
                            {
!                               long    bloblen = 
blob_len(tv_dest->vval.v_blob);
  
                                if (check_blob_index(bloblen,
                                                             n1, FALSE) == FAIL
--- 2569,2575 ----
                                status = FAIL;
                            else
                            {
!                               long  bloblen = blob_len(tv_dest->vval.v_blob);
  
                                if (check_blob_index(bloblen,
                                                             n1, FALSE) == FAIL
***************
*** 2543,2548 ****
--- 2582,2592 ----
                            }
                        }
                    }
+                   else
+                   {
+                       status = FAIL;
+                       emsg(_(e_blob_required));
+                   }
  
                    clear_tv(tv_idx1);
                    clear_tv(tv_idx2);
***************
*** 5469,5475 ****
            case ISN_ANYINDEX: smsg("%s%4d ANYINDEX", pfx, current); break;
            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 ?
--- 5513,5519 ----
            case ISN_ANYINDEX: smsg("%s%4d ANYINDEX", pfx, current); break;
            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 ?
***************
*** 5490,5496 ****
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off);
                      else
!                         smsg("%s%4d CHECKTYPE %s stack[%d] arg %d", pfx, 
current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off,
                                          (int)ct->ct_arg_idx);
--- 5534,5541 ----
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off);
                      else
!                         smsg("%s%4d CHECKTYPE %s stack[%d] arg %d",
!                                         pfx, current,
                                          type_name(ct->ct_type, &tofree),
                                          (int)ct->ct_off,
                                          (int)ct->ct_arg_idx);
*** ../vim-8.2.3331/src/list.c  2021-08-09 19:59:01.442811242 +0200
--- src/list.c  2021-08-11 21:41:14.040726023 +0200
***************
*** 762,767 ****
--- 762,919 ----
  }
  
  /*
+  * Get the list item in "l" with index "n1".  "n1" is adjusted if needed.
+  * In Vim9, it is at the end of the list, add an item.
+  * Return NULL if there is no such item.
+  */
+     listitem_T *
+ check_range_index_one(list_T *l, long *n1, int quiet)
+ {
+     listitem_T *li = list_find_index(l, n1);
+ 
+     if (li == NULL)
+     {
+       // Vim9: Allow for adding an item at the end.
+       if (in_vim9script() && *n1 == l->lv_len && l->lv_lock == 0)
+       {
+           list_append_number(l, 0);
+           li = list_find_index(l, n1);
+       }
+       if (li == NULL)
+       {
+           if (!quiet)
+               semsg(_(e_listidx), *n1);
+           return NULL;
+       }
+     }
+     return li;
+ }
+ 
+ /*
+  * Check that "n2" can be used as the second index in a range of list "l".
+  * If "n1" or "n2" is negative it is changed to the positive index.
+  * "li1" is the item for item "n1".
+  * Return OK or FAIL.
+  */
+     int
+ check_range_index_two(
+       list_T      *l,
+       long        *n1,
+       listitem_T  *li1,
+       long        *n2,
+       int         quiet)
+ {
+     if (*n2 < 0)
+     {
+       listitem_T      *ni = list_find(l, *n2);
+ 
+       if (ni == NULL)
+       {
+           if (!quiet)
+               semsg(_(e_listidx), *n2);
+           return FAIL;
+       }
+       *n2 = list_idx_of_item(l, ni);
+     }
+ 
+     // Check that n2 isn't before n1.
+     if (*n1 < 0)
+       *n1 = list_idx_of_item(l, li1);
+     if (*n2 < *n1)
+     {
+       if (!quiet)
+           semsg(_(e_listidx), *n2);
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
+  * Assign values from list "src" into a range of "dest".
+  * "idx1_arg" is the index of the first item in "dest" to be replaced.
+  * "idx2" is the index of last item to be replaced, but when "empty_idx2" is
+  * TRUE then replace all items after "idx1".
+  * "op" is the operator, normally "=" but can be "+=" and the like.
+  * "varname" is used for error messages.
+  * Returns OK or FAIL.
+  */
+     int
+ list_assign_range(
+       list_T      *dest,
+       list_T      *src,
+       long        idx1_arg,
+       long        idx2,
+       int         empty_idx2,
+       char_u      *op,
+       char_u      *varname)
+ {
+     listitem_T        *src_li;
+     listitem_T        *dest_li;
+     long      idx1 = idx1_arg;
+     listitem_T        *first_li = list_find_index(dest, &idx1);
+     long      idx;
+ 
+     /*
+      * Check whether any of the list items is locked before making any 
changes.
+      */
+     idx = idx1;
+     dest_li = first_li;
+     for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
+     {
+       if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
+           return FAIL;
+       src_li = src_li->li_next;
+       if (src_li == NULL || (!empty_idx2 && idx2 == idx))
+           break;
+       dest_li = dest_li->li_next;
+       ++idx;
+     }
+ 
+     /*
+      * Assign the List values to the list items.
+      */
+     idx = idx1;
+     dest_li = first_li;
+     for (src_li = src->lv_first; src_li != NULL; )
+     {
+       if (op != NULL && *op != '=')
+           tv_op(&dest_li->li_tv, &src_li->li_tv, op);
+       else
+       {
+           clear_tv(&dest_li->li_tv);
+           copy_tv(&src_li->li_tv, &dest_li->li_tv);
+       }
+       src_li = src_li->li_next;
+       if (src_li == NULL || (!empty_idx2 && idx2 == idx))
+           break;
+       if (dest_li->li_next == NULL)
+       {
+           // Need to add an empty item.
+           if (list_append_number(dest, 0) == FAIL)
+           {
+               src_li = NULL;
+               break;
+           }
+       }
+       dest_li = dest_li->li_next;
+       ++idx;
+     }
+     if (src_li != NULL)
+     {
+       emsg(_(e_list_value_has_more_items_than_targets));
+       return FAIL;
+     }
+     if (empty_idx2
+           ? (dest_li != NULL && dest_li->li_next != NULL)
+           : idx != idx2)
+     {
+       emsg(_(e_list_value_does_not_have_enough_items));
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
   * Flatten "list" to depth "maxdepth".
   * It does nothing if "maxdepth" is 0.
   * Returns FAIL when out of memory.
*** ../vim-8.2.3331/src/proto/list.pro  2021-08-09 19:59:01.442811242 +0200
--- src/proto/list.pro  2021-08-11 21:41:18.316713385 +0200
***************
*** 30,35 ****
--- 30,38 ----
  int list_append_number(list_T *l, varnumber_T n);
  int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item);
  void list_insert(list_T *l, listitem_T *ni, listitem_T *item);
+ listitem_T *check_range_index_one(list_T *l, long *n1, int quiet);
+ int check_range_index_two(list_T *l, long *n1, listitem_T *li1, long *n2, int 
quiet);
+ int list_assign_range(list_T *dest, list_T *src, long idx1_arg, long idx2, 
int empty_idx2, char_u *op, char_u *varname);
  void f_flatten(typval_T *argvars, typval_T *rettv);
  void f_flattennew(typval_T *argvars, typval_T *rettv);
  int list_extend(list_T *l1, list_T *l2, listitem_T *bef);
*** ../vim-8.2.3331/src/eval.c  2021-08-05 20:39:59.350053671 +0200
--- src/eval.c  2021-08-11 21:41:29.288680935 +0200
***************
*** 45,51 ****
      int               fi_byte_idx;    // byte index in fi_string
  } forinfo_T;
  
- static int tv_op(typval_T *tv1, typval_T *tv2, char_u  *op);
  static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
  static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
  static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
--- 45,50 ----
***************
*** 827,833 ****
      typval_T  var1;
      typval_T  var2;
      int               empty1 = FALSE;
-     listitem_T        *ni;
      char_u    *key = NULL;
      int               len;
      hashtab_T *ht = NULL;
--- 826,831 ----
***************
*** 1210,1232 ****
  
            lp->ll_dict = NULL;
            lp->ll_list = lp->ll_tv->vval.v_list;
!           lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
            if (lp->ll_li == NULL)
            {
!               // Vim9: Allow for adding an item at the end.
!               if (in_vim9script() && lp->ll_n1 == lp->ll_list->lv_len
!                                                 && lp->ll_list->lv_lock == 0)
!               {
!                   list_append_number(lp->ll_list, 0);
!                   lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
!               }
!               if (lp->ll_li == NULL)
!               {
!                   clear_tv(&var2);
!                   if (!quiet)
!                       semsg(_(e_listidx), lp->ll_n1);
!                   return NULL;
!               }
            }
  
            if (lp->ll_valtype != NULL)
--- 1208,1218 ----
  
            lp->ll_dict = NULL;
            lp->ll_list = lp->ll_tv->vval.v_list;
!           lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
            if (lp->ll_li == NULL)
            {
!               clear_tv(&var2);
!               return NULL;
            }
  
            if (lp->ll_valtype != NULL)
***************
*** 1244,1270 ****
                lp->ll_n2 = (long)tv_get_number(&var2);
                                                    // is number or string
                clear_tv(&var2);
!               if (lp->ll_n2 < 0)
!               {
!                   ni = list_find(lp->ll_list, lp->ll_n2);
!                   if (ni == NULL)
!                   {
!                       if (!quiet)
!                           semsg(_(e_listidx), lp->ll_n2);
!                       return NULL;
!                   }
!                   lp->ll_n2 = list_idx_of_item(lp->ll_list, ni);
!               }
! 
!               // Check that lp->ll_n2 isn't before lp->ll_n1.
!               if (lp->ll_n1 < 0)
!                   lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li);
!               if (lp->ll_n2 < lp->ll_n1)
!               {
!                   if (!quiet)
!                       semsg(_(e_listidx), lp->ll_n2);
                    return NULL;
-               }
            }
  
            lp->ll_tv = &lp->ll_li->li_tv;
--- 1230,1239 ----
                lp->ll_n2 = (long)tv_get_number(&var2);
                                                    // is number or string
                clear_tv(&var2);
!               if (check_range_index_two(lp->ll_list,
!                                           &lp->ll_n1, lp->ll_li,
!                                           &lp->ll_n2, quiet) == FAIL)
                    return NULL;
            }
  
            lp->ll_tv = &lp->ll_li->li_tv;
***************
*** 1303,1309 ****
      int               var_idx)    // index for "let [a, b] = list"
  {
      int               cc;
-     listitem_T        *ri;
      dictitem_T        *di;
  
      if (lp->ll_tv == NULL)
--- 1272,1277 ----
***************
*** 1383,1391 ****
        ;
      else if (lp->ll_range)
      {
-       listitem_T *ll_li = lp->ll_li;
-       int         ll_n1 = lp->ll_n1;
- 
        if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
                                             && (flags & ASSIGN_FOR_LOOP) == 0)
        {
--- 1351,1356 ----
***************
*** 1393,1445 ****
            return;
        }
  
!       /*
!        * Check whether any of the list items is locked
!        */
!       for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; )
!       {
!           if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
!               return;
!           ri = ri->li_next;
!           if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1))
!               break;
!           ll_li = ll_li->li_next;
!           ++ll_n1;
!       }
! 
!       /*
!        * Assign the List values to the list items.
!        */
!       for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
!       {
!           if (op != NULL && *op != '=')
!               tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
!           else
!           {
!               clear_tv(&lp->ll_li->li_tv);
!               copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
!           }
!           ri = ri->li_next;
!           if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
!               break;
!           if (lp->ll_li->li_next == NULL)
!           {
!               // Need to add an empty item.
!               if (list_append_number(lp->ll_list, 0) == FAIL)
!               {
!                   ri = NULL;
!                   break;
!               }
!           }
!           lp->ll_li = lp->ll_li->li_next;
!           ++lp->ll_n1;
!       }
!       if (ri != NULL)
!           emsg(_(e_list_value_has_more_items_than_targets));
!       else if (lp->ll_empty2
!               ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
!               : lp->ll_n1 != lp->ll_n2)
!           emsg(_(e_list_value_does_not_have_enough_items));
      }
      else
      {
--- 1358,1365 ----
            return;
        }
  
!       (void)list_assign_range(lp->ll_list, rettv->vval.v_list,
!                        lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name);
      }
      else
      {
***************
*** 1507,1513 ****
   * and "tv1 .= tv2"
   * Returns OK or FAIL.
   */
!     static int
  tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
  {
      varnumber_T       n;
--- 1427,1433 ----
   * and "tv1 .= tv2"
   * Returns OK or FAIL.
   */
!     int
  tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
  {
      varnumber_T       n;
*** ../vim-8.2.3331/src/proto/eval.pro  2021-07-10 21:28:55.327050110 +0200
--- src/proto/eval.pro  2021-08-11 19:21:22.061939013 +0200
***************
*** 26,31 ****
--- 26,32 ----
  char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int 
skip, int flags, int fne_flags);
  void clear_lval(lval_T *lp);
  void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int 
flags, char_u *op, int var_idx);
+ int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
  void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, evalarg_T *evalarg);
  void skip_for_lines(void *fi_void, evalarg_T *evalarg);
  int next_for_item(void *fi_void, char_u *arg);
*** ../vim-8.2.3331/src/testdir/test_listdict.vim       2021-07-27 
22:00:39.753712380 +0200
--- src/testdir/test_listdict.vim       2021-08-11 20:50:07.721236929 +0200
***************
*** 164,174 ****
  
  " test for range assign
  func Test_list_range_assign()
!   let l = [0]
!   let l[:] = [1, 2]
!   call assert_equal([1, 2], l)
!   let l[-4:-1] = [5, 6]
!   call assert_equal([5, 6], l)
  endfunc
  
  " Test removing items in list
--- 164,177 ----
  
  " test for range assign
  func Test_list_range_assign()
!   let lines =<< trim END
!       VAR l = [0]
!       LET l[:] = [1, 2]
!       call assert_equal([1, 2], l)
!       LET l[-4 : -1] = [5, 6]
!       call assert_equal([5, 6], l)
!   END
!   call CheckLegacyAndVim9Success(lines)
  endfunc
  
  " Test removing items in list
*** ../vim-8.2.3331/src/testdir/test_vim9_assign.vim    2021-08-07 
16:30:35.109065179 +0200
--- src/testdir/test_vim9_assign.vim    2021-08-11 20:47:38.117656303 +0200
***************
*** 1821,1827 ****
    CheckDefFailure([
      'var ll = [1, 2]',
      'll[1 : 2] = 7',
!     ], 'E1165:', 2)
    CheckDefFailure([
      'var dd = {a: 1}',
      'unlet dd["a" : "a"]',
--- 1821,1827 ----
    CheckDefFailure([
      'var ll = [1, 2]',
      'll[1 : 2] = 7',
!     ], 'E1012: Type mismatch; expected list<number> but got number', 2)
    CheckDefFailure([
      'var dd = {a: 1}',
      'unlet dd["a" : "a"]',
*** ../vim-8.2.3331/src/version.c       2021-08-11 17:13:49.548650233 +0200
--- src/version.c       2021-08-11 18:14:33.218960489 +0200
***************
*** 757,758 ****
--- 757,760 ----
  {   /* Add new patch number below this line */
+ /**/
+     3332,
  /**/

-- 
There can't be a crisis today, my schedule is already full.

 /// 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/202108111950.17BJo33f3015056%40masaka.moolenaar.net.

Raspunde prin e-mail lui