# HG changeset patch # User Bram Moolenaar # Date 1602185403 -7200 # Node ID 35ef9b0a81a31b4b2c52ef941f975ed1c061d130 # Parent 61f656177beddad6f2a6ebaa52d8d3c805ec2c14 patch 8.2.1813: Vim9: can assign wrong type to script dict Commit: https://github.com/vim/vim/commit/10c65860f83589e0ca2498393d3cfef1115b7fe8 Author: Bram Moolenaar Date: Thu Oct 8 21:16:42 2020 +0200 patch 8.2.1813: Vim9: can assign wrong type to script dict Problem: Vim9: can assign wrong type to script dict. (Christian J. Robinson) Solution: Check the type if known. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -887,6 +887,17 @@ get_lval( return NULL; } + if (in_vim9script() && lp->ll_valtype == NULL + && lp->ll_tv == &v->di_tv + && ht != NULL && ht == get_script_local_ht()) + { + svar_T *sv = find_typval_in_script(lp->ll_tv); + + // Vim9 script local variable: get the type + if (sv != NULL) + lp->ll_valtype = sv->sv_type; + } + len = -1; if (*p == '.') { @@ -1037,6 +1048,10 @@ get_lval( } } + if (lp->ll_valtype != NULL) + // use the type of the member + lp->ll_valtype = lp->ll_valtype->tt_member; + if (lp->ll_di == NULL) { // Can't add "v:" or "a:" variable. @@ -1148,6 +1163,10 @@ get_lval( return NULL; } + if (lp->ll_valtype != NULL) + // use the type of the member + lp->ll_valtype = lp->ll_valtype->tt_member; + /* * May need to find the item or absolute index for the second * index of a range. @@ -1383,6 +1402,11 @@ set_var_lval( emsg(_("E996: Cannot lock a list or dict")); return; } + + if (lp->ll_valtype != NULL + && check_typval_type(lp->ll_valtype, rettv, 0) == FAIL) + return; + if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -58,6 +58,7 @@ int eval_variable(char_u *name, int len, void check_vars(char_u *name, int len); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); +hashtab_T *get_script_local_ht(void); void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy); hashtab_T *find_var_ht(char_u *name, char_u **varname); char_u *get_var_value(char_u *name); diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -8,5 +8,6 @@ void ex_import(exarg_T *eap); int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type); 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); +svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4055,6 +4055,7 @@ typedef struct lval_S dict_T *ll_dict; // The Dictionary or NULL dictitem_T *ll_di; // The dictitem or NULL char_u *ll_newkey; // New key for Dict in alloc. mem or NULL. + type_T *ll_valtype; // type expected for the value or NULL blob_T *ll_blob; // The Blob or NULL } lval_T; diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -145,6 +145,15 @@ def Test_wrong_type() CheckDefFailure(['var Ref: string', 'var res = Ref()'], 'E1085:') enddef +def Test_script_wrong_type() + var lines =<< trim END + vim9script + var s:dict: dict + s:dict['a'] = ['x'] + END + CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got list', 3) +enddef + def Test_const() CheckDefFailure(['final name = 234', 'name = 99'], 'E1018:') CheckDefFailure(['final one = 234', 'var one = 99'], 'E1017:') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1813, +/**/ 1812, /**/ 1811, diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -565,18 +565,18 @@ vim9_declare_scriptvar(exarg_T *eap, cha } /* - * Check if the type of script variable "dest" allows assigning "value". - * If needed convert "value" to a bool. + * Find the script-local variable that links to "dest". + * Returns NULL if not found. */ - int -check_script_var_type(typval_T *dest, typval_T *value, char_u *name) + svar_T * +find_typval_in_script(typval_T *dest) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int idx; if (si->sn_version != SCRIPT_VERSION_VIM9) // legacy script doesn't store variable types - return OK; + return NULL; // Find the svar_T in sn_var_vals. for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) @@ -584,28 +584,42 @@ check_script_var_type(typval_T *dest, ty svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; if (sv->sv_tv == dest) - { - int ret; - - if (sv->sv_const) - { - semsg(_(e_readonlyvar), name); - return FAIL; - } - ret = check_typval_type(sv->sv_type, value, 0); - if (ret == OK && need_convert_to_bool(sv->sv_type, value)) - { - int val = tv2bool(value); - - clear_tv(value); - value->v_type = VAR_BOOL; - value->v_lock = 0; - value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; - } - return ret; - } + return sv; } iemsg("check_script_var_type(): not found"); + return NULL; +} + +/* + * Check if the type of script variable "dest" allows assigning "value". + * If needed convert "value" to a bool. + */ + int +check_script_var_type(typval_T *dest, typval_T *value, char_u *name) +{ + svar_T *sv = find_typval_in_script(dest); + int ret; + + if (sv != NULL) + { + if (sv->sv_const) + { + semsg(_(e_readonlyvar), name); + return FAIL; + } + ret = check_typval_type(sv->sv_type, value, 0); + if (ret == OK && need_convert_to_bool(sv->sv_type, value)) + { + int val = tv2bool(value); + + clear_tv(value); + value->v_type = VAR_BOOL; + value->v_lock = 0; + value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; + } + return ret; + } + return OK; // not really }