Patch 8.2.1473
Problem:    Items in a list given to :const can still be modified.
Solution:   Work like ":lockvar! name" but don't lock referenced items.
            Make locking a blob work.
Files:      runtime/doc/eval.txt, src/evalvars.c, src/eval.c,
            src/testdir/test_const.vim


*** ../vim-8.2.1472/runtime/doc/eval.txt        2020-08-15 18:38:32.072814243 
+0200
--- runtime/doc/eval.txt        2020-08-17 20:39:11.291181532 +0200
***************
*** 12201,12210 ****
                                :const x = 1
  <                     is equivalent to: >
                                :let x = 1
!                               :lockvar 1 x
  <                     This is useful if you want to make sure the variable
!                       is not modified.
!                                                       *E995*
                        |:const| does not allow to for changing a variable: >
                                :let x = 1
                                :const x = 2  " Error!
--- 12264,12281 ----
                                :const x = 1
  <                     is equivalent to: >
                                :let x = 1
!                               :lockvar! x
  <                     This is useful if you want to make sure the variable
!                       is not modified.  If the value is a List or Dictionary
!                       literal then the items also cannot be changed: >
!                               const ll = [1, 2, 3]
!                               let ll[1] = 5  " Error!
! <                     Nested references are not locked: >
!                               let lvar = ['a']
!                               const lconst = [0, lvar]
!                               let lconst[0] = 2  " Error!
!                               let lconst[1][0] = 'b'  " OK
! <                                                     *E995*
                        |:const| does not allow to for changing a variable: >
                                :let x = 1
                                :const x = 2  " Error!
*** ../vim-8.2.1472/src/evalvars.c      2020-08-16 22:49:57.393356184 +0200
--- src/evalvars.c      2020-08-17 20:41:59.898237787 +0200
***************
*** 173,179 ****
  static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, 
char_u *endchars, char_u *op);
  static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
  static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
! static void item_lock(typval_T *tv, int deep, int lock);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char *prefix, int *first);
  static void list_one_var_a(char *prefix, char_u *name, int type, char_u 
*string, int *first);
--- 173,179 ----
  static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, 
char_u *endchars, char_u *op);
  static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
  static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
! static void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char *prefix, int *first);
  static void list_one_var_a(char *prefix, char_u *name, int type, char_u 
*string, int *first);
***************
*** 1703,1709 ****
                    di->di_flags |= DI_FLAGS_LOCK;
                else
                    di->di_flags &= ~DI_FLAGS_LOCK;
!               item_lock(&di->di_tv, deep, lock);
            }
        }
        *name_end = cc;
--- 1703,1709 ----
                    di->di_flags |= DI_FLAGS_LOCK;
                else
                    di->di_flags &= ~DI_FLAGS_LOCK;
!               item_lock(&di->di_tv, deep, lock, FALSE);
            }
        }
        *name_end = cc;
***************
*** 1715,1740 ****
        // (un)lock a range of List items.
        while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
        {
!           item_lock(&li->li_tv, deep, lock);
            li = li->li_next;
            ++lp->ll_n1;
        }
      }
      else if (lp->ll_list != NULL)
        // (un)lock a List item.
!       item_lock(&lp->ll_li->li_tv, deep, lock);
      else
        // (un)lock a Dictionary item.
!       item_lock(&lp->ll_di->di_tv, deep, lock);
  
      return ret;
  }
  
  /*
   * Lock or unlock an item.  "deep" is nr of levels to go.
   */
      static void
! item_lock(typval_T *tv, int deep, int lock)
  {
      static int        recurse = 0;
      list_T    *l;
--- 1715,1742 ----
        // (un)lock a range of List items.
        while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
        {
!           item_lock(&li->li_tv, deep, lock, FALSE);
            li = li->li_next;
            ++lp->ll_n1;
        }
      }
      else if (lp->ll_list != NULL)
        // (un)lock a List item.
!       item_lock(&lp->ll_li->li_tv, deep, lock, FALSE);
      else
        // (un)lock a Dictionary item.
!       item_lock(&lp->ll_di->di_tv, deep, lock, FALSE);
  
      return ret;
  }
  
  /*
   * Lock or unlock an item.  "deep" is nr of levels to go.
+  * When "check_refcount" is TRUE do not lock a list or dict with a reference
+  * count larger than 1.
   */
      static void
! item_lock(typval_T *tv, int deep, int lock, int check_refcount)
  {
      static int        recurse = 0;
      list_T    *l;
***************
*** 1776,1782 ****
            break;
  
        case VAR_BLOB:
!           if ((b = tv->vval.v_blob) != NULL)
            {
                if (lock)
                    b->bv_lock |= VAR_LOCKED;
--- 1778,1785 ----
            break;
  
        case VAR_BLOB:
!           if ((b = tv->vval.v_blob) != NULL
!                                   && !(check_refcount && b->bv_refcount > 1))
            {
                if (lock)
                    b->bv_lock |= VAR_LOCKED;
***************
*** 1785,1791 ****
            }
            break;
        case VAR_LIST:
!           if ((l = tv->vval.v_list) != NULL)
            {
                if (lock)
                    l->lv_lock |= VAR_LOCKED;
--- 1788,1795 ----
            }
            break;
        case VAR_LIST:
!           if ((l = tv->vval.v_list) != NULL
!                                   && !(check_refcount && l->lv_refcount > 1))
            {
                if (lock)
                    l->lv_lock |= VAR_LOCKED;
***************
*** 1794,1804 ****
                if ((deep < 0 || deep > 1) && l->lv_first != &range_list_item)
                    // recursive: lock/unlock the items the List contains
                    FOR_ALL_LIST_ITEMS(l, li)
!                       item_lock(&li->li_tv, deep - 1, lock);
            }
            break;
        case VAR_DICT:
!           if ((d = tv->vval.v_dict) != NULL)
            {
                if (lock)
                    d->dv_lock |= VAR_LOCKED;
--- 1798,1809 ----
                if ((deep < 0 || deep > 1) && l->lv_first != &range_list_item)
                    // recursive: lock/unlock the items the List contains
                    FOR_ALL_LIST_ITEMS(l, li)
!                       item_lock(&li->li_tv, deep - 1, lock, check_refcount);
            }
            break;
        case VAR_DICT:
!           if ((d = tv->vval.v_dict) != NULL
!                                   && !(check_refcount && d->dv_refcount > 1))
            {
                if (lock)
                    d->dv_lock |= VAR_LOCKED;
***************
*** 1813,1819 ****
                        if (!HASHITEM_EMPTY(hi))
                        {
                            --todo;
!                           item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
                        }
                    }
                }
--- 1818,1825 ----
                        if (!HASHITEM_EMPTY(hi))
                        {
                            --todo;
!                           item_lock(&HI2DI(hi)->di_tv, deep - 1, lock,
!                                                              check_refcount);
                        }
                    }
                }
***************
*** 3087,3093 ****
      }
  
      if (flags & LET_IS_CONST)
!       item_lock(&di->di_tv, 1, TRUE);
  }
  
  /*
--- 3093,3102 ----
      }
  
      if (flags & LET_IS_CONST)
!       // Like :lockvar! name: lock the value and what it contains, but only
!       // if the reference count is up to one.  That locks only literal
!       // values.
!       item_lock(&di->di_tv, DICT_MAXNEST, TRUE, TRUE);
  }
  
  /*
*** ../vim-8.2.1472/src/eval.c  2020-08-16 18:42:50.678811797 +0200
--- src/eval.c  2020-08-17 20:58:12.160440884 +0200
***************
*** 1218,1223 ****
--- 1218,1225 ----
                semsg(_(e_letwrong), op);
                return;
            }
+           if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE))
+               return;
  
            if (lp->ll_range && rettv->v_type == VAR_BLOB)
            {
*** ../vim-8.2.1472/src/testdir/test_const.vim  2020-08-16 22:49:57.393356184 
+0200
--- src/testdir/test_const.vim  2020-08-17 20:47:56.420170904 +0200
***************
*** 273,292 ****
      call assert_fails('const {s2} = "bar"', 'E995:')
  endfunc
  
! func Test_lock_depth_is_1()
!     const l = [1, 2, 3]
!     const d = {'foo': 10}
! 
!     " Modify list - setting item is OK, adding/removing items not
!     let l[0] = 42
      call assert_fails('call add(l, 4)', 'E741:')
      call assert_fails('unlet l[1]', 'E741:')
  
!     " Modify dict - changing item is OK, adding/removing items not
!     let d['foo'] = 'hello'
!     let d.foo = 44
      call assert_fails("let d['bar'] = 'hello'", 'E741:')
      call assert_fails("unlet d['foo']", 'E741:')
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
--- 273,307 ----
      call assert_fails('const {s2} = "bar"', 'E995:')
  endfunc
  
! func Test_lock_depth_is_2()
!     " Modify list - error when changing item or adding/removing items
!     const l = [1, 2, [3, 4]]
!     call assert_fails('let l[0] = 42', 'E741:')
!     call assert_fails('let l[2][0] = 42', 'E741:')
      call assert_fails('call add(l, 4)', 'E741:')
      call assert_fails('unlet l[1]', 'E741:')
  
!     " Modify blob - error when changing
!     const b = 0z001122
!     call assert_fails('let b[0] = 42', 'E741:')
! 
!     " Modify dict - error when changing item or adding/removing items
!     const d = {'foo': 10}
!     call assert_fails("let d['foo'] = 'hello'", 'E741:')
!     call assert_fails("let d.foo = 'hello'", 'E741:')
      call assert_fails("let d['bar'] = 'hello'", 'E741:')
      call assert_fails("unlet d['foo']", 'E741:')
+ 
+     " Modifying list or dict item contents is OK.
+     let lvar = ['a', 'b']
+     let bvar = 0z1122
+     const l2 = [0, lvar, bvar]
+     let l2[1][0] = 'c'
+     let l2[2][1] = 0x33
+     call assert_equal([0, ['c', 'b'], 0z1133], l2)
+ 
+     const d2 = #{a: 0, b: lvar, c: 4}
+     let d2.b[1] = 'd'
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.1472/src/version.c       2020-08-17 19:34:06.961427079 +0200
--- src/version.c       2020-08-17 19:56:12.945187908 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1473,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
225. You sign up for free subscriptions for all the computer magazines

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202008171907.07HJ7pDP927366%40masaka.moolenaar.net.

Raspunde prin e-mail lui