Patch 8.2.2331
Problem:    Vim9: wrong error when modifying dict declared with :final.
Solution:   Do not check for writable variable when an index follows.
            (closes #7657)
Files:      src/vim9compile.c, src/structs.h, src/vim9script.c,
            src/proto/vim9script.pro, src/evalvars.c,
            src/testdir/test_vim9_assign.vim


*** ../vim-8.2.2330/src/vim9compile.c   2021-01-11 20:17:30.395385392 +0100
--- src/vim9compile.c   2021-01-11 21:10:47.102931321 +0100
***************
*** 2127,2136 ****
  }
  
  /*
   * Find "name" in script-local items of script "sid".
   * Returns the index in "sn_var_vals" if found.
   * If found but not in "sn_var_vals" returns -1.
!  * If not found returns -2.
   */
      int
  get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx)
--- 2127,2155 ----
  }
  
  /*
+  * If "check_writable" is ASSIGN_CONST give an error if the variable was
+  * defined with :final or :const, if "check_writable" is ASSIGN_FINAL give an
+  * error if the variable was defined with :const.
+  */
+     static int
+ check_item_writable(svar_T *sv, int check_writable, char_u *name)
+ {
+     if ((check_writable == ASSIGN_CONST && sv->sv_const != 0)
+           || (check_writable == ASSIGN_FINAL
+                                             && sv->sv_const == ASSIGN_CONST))
+     {
+       semsg(_(e_readonlyvar), name);
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
   * Find "name" in script-local items of script "sid".
+  * Pass "check_writable" to check_item_writable().
   * Returns the index in "sn_var_vals" if found.
   * If found but not in "sn_var_vals" returns -1.
!  * If not found or the variable is not writable returns -2.
   */
      int
  get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx)
***************
*** 2151,2158 ****
            return -2;
        idx = sav->sav_var_vals_idx;
        sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
!       if (check_writable && sv->sv_const)
!           semsg(_(e_readonlyvar), name);
        return idx;
      }
  
--- 2170,2177 ----
            return -2;
        idx = sav->sav_var_vals_idx;
        sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
!       if (check_item_writable(sv, check_writable, name) == FAIL)
!           return -2;
        return idx;
      }
  
***************
*** 2168,2175 ****
        sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
        if (sv->sv_tv == &di->di_tv)
        {
!           if (check_writable && sv->sv_const)
!               semsg(_(e_readonlyvar), name);
            return idx;
        }
      }
--- 2187,2194 ----
        sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
        if (sv->sv_tv == &di->di_tv)
        {
!           if (check_item_writable(sv, check_writable, name) == FAIL)
!               return -2;
            return idx;
        }
      }
***************
*** 2466,2472 ****
      if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
        return FAIL;
      si = SCRIPT_ITEM(current_sctx.sc_sid);
!     idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx);
      if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9)
      {
        // variable is not in sn_var_vals: old style script.
--- 2485,2491 ----
      if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
        return FAIL;
      si = SCRIPT_ITEM(current_sctx.sc_sid);
!     idx = get_script_item_idx(current_sctx.sc_sid, name, 0, cctx);
      if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9)
      {
        // variable is not in sn_var_vals: old style script.
***************
*** 5475,5480 ****
--- 5494,5504 ----
      lhs->lhs_name = vim_strnsave(var_start, lhs->lhs_varlen);
      if (lhs->lhs_name == NULL)
        return FAIL;
+ 
+     if (lhs->lhs_dest_end > var_start + lhs->lhs_varlen)
+       // Something follows after the variable: "var[idx]" or "var.key".
+       lhs->lhs_has_index = TRUE;
+ 
      if (heredoc)
        lhs->lhs_type = &t_list_string;
      else
***************
*** 5576,5584 ****
                        lhs->lhs_scriptvar_sid = import->imp_sid;
                    if (SCRIPT_ID_VALID(lhs->lhs_scriptvar_sid))
                    {
                        lhs->lhs_scriptvar_idx = get_script_item_idx(
!                                                       lhs->lhs_scriptvar_sid,
!                                                         rawname, TRUE, cctx);
                        if (lhs->lhs_scriptvar_idx >= 0)
                        {
                            scriptitem_T *si = SCRIPT_ITEM(
--- 5600,5610 ----
                        lhs->lhs_scriptvar_sid = import->imp_sid;
                    if (SCRIPT_ID_VALID(lhs->lhs_scriptvar_sid))
                    {
+                       // Check writable only when no index follows.
                        lhs->lhs_scriptvar_idx = get_script_item_idx(
!                                              lhs->lhs_scriptvar_sid, rawname,
!                             lhs->lhs_has_index ? ASSIGN_FINAL : ASSIGN_CONST,
!                                                                        cctx);
                        if (lhs->lhs_scriptvar_idx >= 0)
                        {
                            scriptitem_T *si = SCRIPT_ITEM(
***************
*** 5665,5671 ****
      }
  
      lhs->lhs_member_type = lhs->lhs_type;
!     if (lhs->lhs_dest_end > var_start + lhs->lhs_varlen)
      {
        // Something follows after the variable: "var[idx]" or "var.key".
        // TODO: should we also handle "->func()" here?
--- 5691,5697 ----
      }
  
      lhs->lhs_member_type = lhs->lhs_type;
!     if (lhs->lhs_has_index)
      {
        // Something follows after the variable: "var[idx]" or "var.key".
        // TODO: should we also handle "->func()" here?
***************
*** 5700,5706 ****
                lhs->lhs_type = &t_any;
            }
  
-           lhs->lhs_has_index = TRUE;
            if (lhs->lhs_type->tt_member == NULL)
                lhs->lhs_member_type = &t_any;
            else
--- 5726,5731 ----
*** ../vim-8.2.2330/src/structs.h       2021-01-10 18:33:08.011683523 +0100
--- src/structs.h       2021-01-11 20:49:32.530156868 +0100
***************
*** 1777,1783 ****
      char_u    *sv_name;       // points into "sn_all_vars" di_key
      typval_T  *sv_tv;         // points into "sn_vars" or "sn_all_vars" di_tv
      type_T    *sv_type;
!     int               sv_const;
      int               sv_export;      // "export let var = val"
  };
  
--- 1777,1783 ----
      char_u    *sv_name;       // points into "sn_all_vars" di_key
      typval_T  *sv_tv;         // points into "sn_vars" or "sn_all_vars" di_tv
      type_T    *sv_type;
!     int               sv_const;       // 0, ASSIGN_CONST or ASSIGN_FINAL
      int               sv_export;      // "export let var = val"
  };
  
*** ../vim-8.2.2330/src/vim9script.c    2021-01-02 15:41:00.189079039 +0100
--- src/vim9script.c    2021-01-11 21:17:43.537953743 +0100
***************
*** 257,263 ****
  
      // find name in "script"
      // TODO: also find script-local user function
!     idx = get_script_item_idx(sid, name, FALSE, cctx);
      if (idx >= 0)
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
--- 257,263 ----
  
      // find name in "script"
      // TODO: also find script-local user function
!     idx = get_script_item_idx(sid, name, 0, cctx);
      if (idx >= 0)
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
***************
*** 661,670 ****
   * with a hashtable) and sn_var_vals (lookup by index).
   * When "create" is TRUE this is a new variable, otherwise find and update an
   * existing variable.
   * When "*type" is NULL use "tv" for the type and update "*type".
   */
      void
! update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T 
**type)
  {
      scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
      hashitem_T            *hi;
--- 661,676 ----
   * with a hashtable) and sn_var_vals (lookup by index).
   * When "create" is TRUE this is a new variable, otherwise find and update an
   * existing variable.
+  * "flags" can have ASSIGN_FINAL or ASSIGN_CONST.
   * When "*type" is NULL use "tv" for the type and update "*type".
   */
      void
! update_vim9_script_var(
!       int         create,
!       dictitem_T  *di,
!       int         flags,
!       typval_T    *tv,
!       type_T      **type)
  {
      scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
      hashitem_T            *hi;
***************
*** 686,692 ****
            return;
  
        sv->sv_tv = &di->di_tv;
!       sv->sv_const = (di->di_flags & DI_FLAGS_LOCK) ? ASSIGN_CONST : 0;
        sv->sv_export = is_export;
        newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
        ++si->sn_var_vals.ga_len;
--- 692,699 ----
            return;
  
        sv->sv_tv = &di->di_tv;
!       sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL
!                                  : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0;
        sv->sv_export = is_export;
        newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
        ++si->sn_var_vals.ga_len;
***************
*** 864,870 ****
  
      if (sv != NULL)
      {
!       if (sv->sv_const)
        {
            semsg(_(e_readonlyvar), name);
            return FAIL;
--- 871,877 ----
  
      if (sv != NULL)
      {
!       if (sv->sv_const != 0)
        {
            semsg(_(e_readonlyvar), name);
            return FAIL;
*** ../vim-8.2.2330/src/proto/vim9script.pro    2021-01-02 15:41:00.189079039 
+0100
--- src/proto/vim9script.pro    2021-01-11 21:18:15.993886632 +0100
***************
*** 10,16 ****
  int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, 
cctx_T *cctx);
  char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, 
evalarg_T *evalarg, void *cctx);
  char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
! void update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T 
**type);
  void hide_script_var(scriptitem_T *si, int idx, int func_defined);
  void free_all_script_vars(scriptitem_T *si);
  svar_T *find_typval_in_script(typval_T *dest);
--- 10,16 ----
  int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, 
cctx_T *cctx);
  char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, 
evalarg_T *evalarg, void *cctx);
  char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
! void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T 
*tv, type_T **type);
  void hide_script_var(scriptitem_T *si, int idx, int func_defined);
  void free_all_script_vars(scriptitem_T *si);
  svar_T *find_typval_in_script(typval_T *dest);
*** ../vim-8.2.2330/src/evalvars.c      2021-01-06 21:59:35.174021934 +0100
--- src/evalvars.c      2021-01-11 21:18:04.569910377 +0100
***************
*** 3153,3159 ****
            // A Vim9 script-local variable is also present in sn_all_vars and
            // sn_var_vals.  It may set "type" from "tv".
            if (is_script_local && vim9script)
!               update_vim9_script_var(FALSE, di, tv, &type);
        }
  
        // existing variable, need to clear the value
--- 3153,3159 ----
            // A Vim9 script-local variable is also present in sn_all_vars and
            // sn_var_vals.  It may set "type" from "tv".
            if (is_script_local && vim9script)
!               update_vim9_script_var(FALSE, di, flags, tv, &type);
        }
  
        // existing variable, need to clear the value
***************
*** 3243,3249 ****
        // A Vim9 script-local variable is also added to sn_all_vars and
        // sn_var_vals. It may set "type" from "tv".
        if (is_script_local && vim9script)
!           update_vim9_script_var(TRUE, di, tv, &type);
      }
  
      if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
--- 3243,3249 ----
        // A Vim9 script-local variable is also added to sn_all_vars and
        // sn_var_vals. It may set "type" from "tv".
        if (is_script_local && vim9script)
!           update_vim9_script_var(TRUE, di, flags, tv, &type);
      }
  
      if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
*** ../vim-8.2.2330/src/testdir/test_vim9_assign.vim    2021-01-09 
12:09:18.399881402 +0100
--- src/testdir/test_vim9_assign.vim    2021-01-11 21:12:36.634631486 +0100
***************
*** 1225,1230 ****
--- 1225,1236 ----
        g:dict_val = s:dict[key]
      enddef
      GetDictVal('a')
+ 
+     final adict: dict<string> = {}
+     def ChangeAdict()
+       adict.foo = 'foo'
+     enddef
+     ChangeAdict()
    END
    CheckScriptSuccess(lines)
    assert_equal('', g:var_uninit)
***************
*** 1262,1267 ****
--- 1268,1283 ----
  
    lines =<< trim END
      vim9script
+     const cdict: dict<string> = {}
+     def Change()
+       cdict.foo = 'foo'
+     enddef
+     defcompile
+   END
+   CheckScriptFailure(lines, 'E46:')
+ 
+   lines =<< trim END
+     vim9script
      final w:finalvar = [9]
      w:finalvar = [8]
    END
*** ../vim-8.2.2330/src/version.c       2021-01-11 20:17:30.395385392 +0100
--- src/version.c       2021-01-11 20:41:38.434932966 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2331,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
128. You can access the Net -- via your portable and cellular phone.

 /// 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/202101112020.10BKKofs739992%40masaka.moolenaar.net.

Raspunde prin e-mail lui