To: vim_dev@googlegroups.com Subject: Patch 8.2.2331 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 = {} + 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 = {} + 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 -- Bram@Moolenaar.net -- 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 ///