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)
}
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
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();
{
++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();
{
++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);
goto failed;
if (type == NULL)
type = sv->sv_type;
+ sv->sv_flags |= SVFLAG_ASSIGNED;
}
}
#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.
*/
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 {
enddef
def Test_extend_list()
+ # using uninitilaized list assigns empty list
var lines =<< trim END
var l1: list<number>
var l2 = l1
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<string>
END
v9.CheckScriptSuccess(lines)
+ # initialized to null, with type, does not default to empty list
lines =<< trim END
vim9script
var l: list<string> = test_null_list()
extend(l, ['x'])
- assert_equal(['x'], l)
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
vim9script
vim9script
var d: dict<string> = 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
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<number> = test_null_list()
+ var l: list<number>
add(l, 123)
END
v9.CheckScriptSuccess(lines)
+ # Adding to a variable set to a NULL list fails
+ lines =<< trim END
+ vim9script
+ var l: list<number> = test_null_list()
+ add(l, 123)
+ END
+ v9.CheckScriptFailure(lines, 'E1130:', 3)
+
lines =<< trim END
vim9script
var l: list<string> = ['a']
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 4698,
/**/
4697,
/**/
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);
{
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),
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);
{
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);
{
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);
* 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".
*/
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);
// "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))
// 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;
}
}