0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

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)
This commit is contained in:
Bram Moolenaar 2022-04-05 21:40:38 +01:00
parent 0d1f55c044
commit aa7d0c2335
7 changed files with 71 additions and 24 deletions

View File

@ -2828,13 +2828,18 @@ eval_variable(
} }
else if (rettv != NULL) else if (rettv != NULL)
{ {
svar_T *sv = NULL;
int was_assigned = FALSE;
if (ht != NULL && ht == get_script_local_ht() if (ht != NULL && ht == get_script_local_ht()
&& tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv) && 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) if (sv != NULL)
{
type = sv->sv_type; type = sv->sv_type;
was_assigned = sv->sv_flags & SVFLAG_ASSIGNED;
}
} }
// If a list or dict variable wasn't initialized and has meaningful // If a list or dict variable wasn't initialized and has meaningful
@ -2843,7 +2848,7 @@ eval_variable(
if (ht != &globvarht) if (ht != &globvarht)
{ {
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
&& ((type != NULL && type != &t_dict_empty) && ((type != NULL && !was_assigned)
|| !in_vim9script())) || !in_vim9script()))
{ {
tv->vval.v_dict = dict_alloc(); tv->vval.v_dict = dict_alloc();
@ -2851,10 +2856,12 @@ eval_variable(
{ {
++tv->vval.v_dict->dv_refcount; ++tv->vval.v_dict->dv_refcount;
tv->vval.v_dict->dv_type = alloc_type(type); 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 else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
&& ((type != NULL && type != &t_list_empty) && ((type != NULL && !was_assigned)
|| !in_vim9script())) || !in_vim9script()))
{ {
tv->vval.v_list = list_alloc(); tv->vval.v_list = list_alloc();
@ -2862,15 +2869,21 @@ eval_variable(
{ {
++tv->vval.v_list->lv_refcount; ++tv->vval.v_list->lv_refcount;
tv->vval.v_list->lv_type = alloc_type(type); 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 else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
&& ((type != NULL && type != &t_blob_null) && ((type != NULL && !was_assigned)
|| !in_vim9script())) || !in_vim9script()))
{ {
tv->vval.v_blob = blob_alloc(); tv->vval.v_blob = blob_alloc();
if (tv->vval.v_blob != NULL) if (tv->vval.v_blob != NULL)
{
++tv->vval.v_blob->bv_refcount; ++tv->vval.v_blob->bv_refcount;
if (sv != NULL)
sv->sv_flags |= SVFLAG_ASSIGNED;
}
} }
} }
copy_tv(tv, rettv); copy_tv(tv, rettv);
@ -3587,6 +3600,7 @@ set_var_const(
goto failed; goto failed;
if (type == NULL) if (type == NULL)
type = sv->sv_type; type = sv->sv_type;
sv->sv_flags |= SVFLAG_ASSIGNED;
} }
} }

View File

@ -1807,6 +1807,10 @@ struct sallvar_S {
#define HIKEY2SAV(p) ((sallvar_T *)(p - offsetof(sallvar_T, sav_key))) #define HIKEY2SAV(p) ((sallvar_T *)(p - offsetof(sallvar_T, sav_key)))
#define HI2SAV(hi) HIKEY2SAV((hi)->hi_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. * 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 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 typval_T *sv_tv; // points into "sn_vars" or "sn_all_vars" di_tv
type_T *sv_type; 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_const; // 0, ASSIGN_CONST or ASSIGN_FINAL
int sv_export; // "export let var = val"
}; };
typedef struct { typedef struct {

View File

@ -740,6 +740,7 @@ def Test_init_in_for_loop()
enddef enddef
def Test_extend_list() def Test_extend_list()
# using uninitilaized list assigns empty list
var lines =<< trim END var lines =<< trim END
var l1: list<number> var l1: list<number>
var l2 = l1 var l2 = l1
@ -757,7 +758,7 @@ def Test_extend_list()
END END
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
# appending to NULL list from a function # appending to uninitialzed list from a function works
lines =<< trim END lines =<< trim END
vim9script vim9script
var list: list<string> var list: list<string>
@ -779,13 +780,30 @@ def Test_extend_list()
END END
v9.CheckScriptSuccess(lines) v9.CheckScriptSuccess(lines)
# initialized to null, with type, does not default to empty list
lines =<< trim END lines =<< trim END
vim9script vim9script
var l: list<string> = test_null_list() var l: list<string> = test_null_list()
extend(l, ['x']) extend(l, ['x'])
assert_equal(['x'], l)
END END
v9.CheckScriptSuccess(lines) 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.CheckScriptFailure(lines, 'E1134:', 3)
# assigned null, does not default to empty list
lines =<< trim END
vim9script
var l: list<string>
l = null_list
extend(l, ['x'])
END
v9.CheckScriptFailure(lines, 'E1134:', 4)
lines =<< trim END lines =<< trim END
vim9script vim9script
@ -838,9 +856,8 @@ def Test_extend_dict()
vim9script vim9script
var d: dict<string> = test_null_dict() var d: dict<string> = test_null_dict()
extend(d, {a: 'x'}) extend(d, {a: 'x'})
assert_equal({a: 'x'}, d)
END END
v9.CheckScriptSuccess(lines) v9.CheckScriptFailure(lines, 'E1133:', 3)
lines =<< trim END lines =<< trim END
vim9script vim9script

View File

@ -153,13 +153,21 @@ def Test_add_list()
END END
v9.CheckDefExecFailure(lines, 'E1130:', 2) 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<number>
add(l, 123)
END
v9.CheckScriptSuccess(lines)
# Adding to a variable set to a NULL list fails
lines =<< trim END lines =<< trim END
vim9script vim9script
var l: list<number> = test_null_list() var l: list<number> = test_null_list()
add(l, 123) add(l, 123)
END END
v9.CheckScriptSuccess(lines) v9.CheckScriptFailure(lines, 'E1130:', 3)
lines =<< trim END lines =<< trim END
vim9script vim9script

View File

@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
4698,
/**/ /**/
4697, 4697,
/**/ /**/

View File

@ -1514,7 +1514,8 @@ get_script_svar(scriptref_T *sref, int dfunc_idx)
return NULL; 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) if (dfunc != NULL)
semsg(_(e_item_not_exported_in_script_str), sv->sv_name); 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) sv = ((svar_T *)SCRIPT_ITEM(sid)
->sn_var_vals.ga_data) + idx; ->sn_var_vals.ga_data) + idx;
if (!sv->sv_export) if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
{ {
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_item_not_exported_in_script_str), 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) svar_T *sv = ((svar_T *)SCRIPT_ITEM(sid)
->sn_var_vals.ga_data) + idx; ->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), semsg(_(e_item_not_exported_in_script_str),
name); name);

View File

@ -334,7 +334,7 @@ free_all_script_vars(scriptitem_T *si)
{ {
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 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); free_type(sv->sv_type);
} }
ga_clear(&si->sn_var_vals); ga_clear(&si->sn_var_vals);
@ -721,7 +721,7 @@ find_exported(
{ {
sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
*ufunc = NULL; *ufunc = NULL;
if (!sv->sv_export) if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
{ {
if (verbose) if (verbose)
semsg(_(e_item_not_exported_in_script_str), name); semsg(_(e_item_not_exported_in_script_str), name);
@ -871,7 +871,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
* with a hashtable) and sn_var_vals (lookup by index). * with a hashtable) and sn_var_vals (lookup by index).
* When "create" is TRUE this is a new variable, otherwise find and update an * When "create" is TRUE this is a new variable, otherwise find and update an
* existing variable. * 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 * 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". * "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_tv = &di->di_tv;
sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL
: (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0; : (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; newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
++si->sn_var_vals.ga_len; ++si->sn_var_vals.ga_len;
STRCPY(&newsav->sav_key, name); STRCPY(&newsav->sav_key, name);
@ -970,7 +972,7 @@ update_vim9_script_var(
// "var b: blob = null_blob" has a different type. // "var b: blob = null_blob" has a different type.
*type = &t_blob_null; *type = &t_blob_null;
} }
if (sv->sv_type_allocated) if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED)
free_type(sv->sv_type); free_type(sv->sv_type);
if (*type != NULL && ((*type)->tt_type == VAR_FUNC if (*type != NULL && ((*type)->tt_type == VAR_FUNC
|| (*type)->tt_type == VAR_PARTIAL)) || (*type)->tt_type == VAR_PARTIAL))
@ -979,12 +981,12 @@ update_vim9_script_var(
// function is freed, but the script variable may keep the type. // function is freed, but the script variable may keep the type.
// Make a copy to avoid using freed memory. // Make a copy to avoid using freed memory.
sv->sv_type = alloc_type(*type); sv->sv_type = alloc_type(*type);
sv->sv_type_allocated = TRUE; sv->sv_flags |= SVFLAG_TYPE_ALLOCATED;
} }
else else
{ {
sv->sv_type = *type; sv->sv_type = *type;
sv->sv_type_allocated = FALSE; sv->sv_flags &= ~SVFLAG_TYPE_ALLOCATED;
} }
} }