# HG changeset patch # User Bram Moolenaar # Date 1649191503 -7200 # Node ID fabe722b24e98d741e70949ca21c3fe26245d944 # Parent 10a99c04ba1d7055232a8d91b104a12161722fb3 patch 8.2.4698: Vim9: script variable has no flag that it was set Commit: https://github.com/vim/vim/commit/aa7d0c233532fb9d8c2876ea8e978a82b12c377f Author: Bram Moolenaar Date: Tue Apr 5 21:40:38 2022 +0100 patch 8.2.4698: Vim9: script variable has no flag that it was set Problem: Vim9: script variable has no flag that it was set. Solution: Add a flag that it was set, to avoid giving it a value when used. (closes #10088) diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2828,13 +2828,18 @@ eval_variable( } else if (rettv != NULL) { + svar_T *sv = NULL; + int was_assigned = FALSE; + if (ht != NULL && ht == get_script_local_ht() && tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv) { - svar_T *sv = find_typval_in_script(tv, 0, TRUE); - + sv = find_typval_in_script(tv, 0, TRUE); if (sv != NULL) + { type = sv->sv_type; + was_assigned = sv->sv_flags & SVFLAG_ASSIGNED; + } } // If a list or dict variable wasn't initialized and has meaningful @@ -2843,7 +2848,7 @@ eval_variable( if (ht != &globvarht) { if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL - && ((type != NULL && type != &t_dict_empty) + && ((type != NULL && !was_assigned) || !in_vim9script())) { tv->vval.v_dict = dict_alloc(); @@ -2851,10 +2856,12 @@ eval_variable( { ++tv->vval.v_dict->dv_refcount; tv->vval.v_dict->dv_type = alloc_type(type); + if (sv != NULL) + sv->sv_flags |= SVFLAG_ASSIGNED; } } else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL - && ((type != NULL && type != &t_list_empty) + && ((type != NULL && !was_assigned) || !in_vim9script())) { tv->vval.v_list = list_alloc(); @@ -2862,15 +2869,21 @@ eval_variable( { ++tv->vval.v_list->lv_refcount; tv->vval.v_list->lv_type = alloc_type(type); + if (sv != NULL) + sv->sv_flags |= SVFLAG_ASSIGNED; } } else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL - && ((type != NULL && type != &t_blob_null) + && ((type != NULL && !was_assigned) || !in_vim9script())) { tv->vval.v_blob = blob_alloc(); if (tv->vval.v_blob != NULL) + { ++tv->vval.v_blob->bv_refcount; + if (sv != NULL) + sv->sv_flags |= SVFLAG_ASSIGNED; + } } } copy_tv(tv, rettv); @@ -3587,6 +3600,7 @@ set_var_const( goto failed; if (type == NULL) type = sv->sv_type; + sv->sv_flags |= SVFLAG_ASSIGNED; } } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1807,6 +1807,10 @@ struct sallvar_S { #define HIKEY2SAV(p) ((sallvar_T *)(p - offsetof(sallvar_T, sav_key))) #define HI2SAV(hi) HIKEY2SAV((hi)->hi_key) +#define SVFLAG_TYPE_ALLOCATED 1 // call free_type() for "sv_type" +#define SVFLAG_EXPORTED 2 // "export let var = val" +#define SVFLAG_ASSIGNED 4 // assigned a value + /* * Entry for "sn_var_vals". Used for script-local variables. */ @@ -1814,9 +1818,8 @@ struct svar_S { 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_type_allocated; // call free_type() for sv_type + int sv_flags; // SVFLAG_ values above int sv_const; // 0, ASSIGN_CONST or ASSIGN_FINAL - int sv_export; // "export let var = val" }; typedef struct { diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -740,6 +740,7 @@ def Test_init_in_for_loop() enddef def Test_extend_list() + # using uninitilaized list assigns empty list var lines =<< trim END var l1: list var l2 = l1 @@ -757,7 +758,7 @@ def Test_extend_list() END v9.CheckDefAndScriptSuccess(lines) - # appending to NULL list from a function + # appending to uninitialzed list from a function works lines =<< trim END vim9script var list: list @@ -779,13 +780,30 @@ def Test_extend_list() END v9.CheckScriptSuccess(lines) + # initialized to null, with type, does not default to empty list lines =<< trim END vim9script var l: list = test_null_list() extend(l, ['x']) - assert_equal(['x'], l) + END + v9.CheckScriptFailure(lines, 'E1134:', 3) + + # initialized to null, without type, does not default to empty list + lines =<< trim END + vim9script + var l = null_list + extend(l, ['x']) END - v9.CheckScriptSuccess(lines) + v9.CheckScriptFailure(lines, 'E1134:', 3) + + # assigned null, does not default to empty list + lines =<< trim END + vim9script + var l: list + l = null_list + extend(l, ['x']) + END + v9.CheckScriptFailure(lines, 'E1134:', 4) lines =<< trim END vim9script @@ -838,9 +856,8 @@ def Test_extend_dict() vim9script var d: dict = test_null_dict() extend(d, {a: 'x'}) - assert_equal({a: 'x'}, d) END - v9.CheckScriptSuccess(lines) + v9.CheckScriptFailure(lines, 'E1133:', 3) lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -153,13 +153,21 @@ def Test_add_list() END v9.CheckDefExecFailure(lines, 'E1130:', 2) - # Getting variable with NULL list allocates a new list at script level + # Getting an uninitialized variable allocates a new list at script level + lines =<< trim END + vim9script + var l: list + add(l, 123) + END + v9.CheckScriptSuccess(lines) + + # Adding to a variable set to a NULL list fails lines =<< trim END vim9script var l: list = test_null_list() add(l, 123) END - v9.CheckScriptSuccess(lines) + v9.CheckScriptFailure(lines, 'E1130:', 3) lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4698, +/**/ 4697, /**/ 4696, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1514,7 +1514,8 @@ get_script_svar(scriptref_T *sref, int d return NULL; } - if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid) + if ((sv->sv_flags & SVFLAG_EXPORTED) == 0 + && sref->sref_sid != current_sctx.sc_sid) { if (dfunc != NULL) semsg(_(e_item_not_exported_in_script_str), sv->sv_name); @@ -2952,7 +2953,7 @@ exec_instructions(ectx_T *ectx) { sv = ((svar_T *)SCRIPT_ITEM(sid) ->sn_var_vals.ga_data) + idx; - if (!sv->sv_export) + if ((sv->sv_flags & SVFLAG_EXPORTED) == 0) { SOURCING_LNUM = iptr->isn_lnum; semsg(_(e_item_not_exported_in_script_str), @@ -3117,7 +3118,7 @@ exec_instructions(ectx_T *ectx) svar_T *sv = ((svar_T *)SCRIPT_ITEM(sid) ->sn_var_vals.ga_data) + idx; - if (!sv->sv_export) + if ((sv->sv_flags & SVFLAG_EXPORTED) == 0) { semsg(_(e_item_not_exported_in_script_str), name); diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -334,7 +334,7 @@ free_all_script_vars(scriptitem_T *si) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; - if (sv->sv_type_allocated) + if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED) free_type(sv->sv_type); } ga_clear(&si->sn_var_vals); @@ -721,7 +721,7 @@ find_exported( { sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; *ufunc = NULL; - if (!sv->sv_export) + if ((sv->sv_flags & SVFLAG_EXPORTED) == 0) { if (verbose) semsg(_(e_item_not_exported_in_script_str), name); @@ -871,7 +871,7 @@ vim9_declare_scriptvar(exarg_T *eap, cha * 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. + * "flags" can have ASSIGN_FINAL, ASSIGN_CONST or ASSIGN_INIT. * When "*type" is NULL use "tv" for the type and update "*type". If * "do_member" is TRUE also use the member type, otherwise use "any". */ @@ -938,7 +938,9 @@ update_vim9_script_var( 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; + sv->sv_flags = is_export ? SVFLAG_EXPORTED : 0; + if ((flags & ASSIGN_INIT) == 0) + sv->sv_flags |= SVFLAG_ASSIGNED; newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; ++si->sn_var_vals.ga_len; STRCPY(&newsav->sav_key, name); @@ -970,7 +972,7 @@ update_vim9_script_var( // "var b: blob = null_blob" has a different type. *type = &t_blob_null; } - if (sv->sv_type_allocated) + if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED) free_type(sv->sv_type); if (*type != NULL && ((*type)->tt_type == VAR_FUNC || (*type)->tt_type == VAR_PARTIAL)) @@ -979,12 +981,12 @@ update_vim9_script_var( // function is freed, but the script variable may keep the type. // Make a copy to avoid using freed memory. sv->sv_type = alloc_type(*type); - sv->sv_type_allocated = TRUE; + sv->sv_flags |= SVFLAG_TYPE_ALLOCATED; } else { sv->sv_type = *type; - sv->sv_type_allocated = FALSE; + sv->sv_flags &= ~SVFLAG_TYPE_ALLOCATED; } }