Problem: Vim9: in :def function script var cannot be null.
Solution: Only initialize a script variable when not set to a null value.
(closes #10034)
{
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
&& ((type != NULL && type != &t_dict_empty)
- || !in_vim9script()))
+ || !in_vim9script()))
{
tv->vval.v_dict = dict_alloc();
if (tv->vval.v_dict != NULL)
tv->vval.v_list->lv_type = alloc_type(type);
}
}
+ else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
+ && ((type != NULL && type != &t_blob_null)
+ || !in_vim9script()))
+ {
+ tv->vval.v_blob = blob_alloc();
+ if (tv->vval.v_blob != NULL)
+ ++tv->vval.v_blob->bv_refcount;
+ }
}
copy_tv(tv, rettv);
}
EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_blob_null INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, &t_void, NULL);
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
unlet g:null_dict
unlet g:not_null_list
+ # variables declared at script level used in a :def function
+ lines =<< trim END
+ vim9script
+
+ var l_decl: list<number>
+ var l_empty = []
+ var l_null = null_list
+
+ def TestList()
+ assert_false(l_decl == null)
+ assert_false(l_decl is null_list)
+ assert_false(l_empty == null)
+ assert_false(l_empty is null_list)
+ assert_true(l_null == null)
+ assert_true(l_null is null_list)
+ assert_true(l_null == null_list)
+
+ add(l_decl, 6)
+ assert_equal([6], l_decl)
+ add(l_empty, 7)
+ assert_equal([7], l_empty)
+ var caught = false
+ try
+ add(l_null, 9)
+ catch /E1130:/
+ caught = true
+ endtry
+ assert_true(caught)
+ enddef
+ TestList()
+
+ var b_decl: blob
+ var b_empty = 0z
+ var b_null = null_blob
+
+ def TestBlob()
+ assert_false(b_decl == null)
+ assert_false(b_decl is null_blob)
+ assert_false(b_empty == null)
+ assert_false(b_empty is null_blob)
+ assert_true(b_null == null)
+ assert_true(b_null is null_blob)
+ assert_true(b_null == null_blob)
+
+ add(b_decl, 6)
+ assert_equal(0z06, b_decl)
+ add(b_empty, 7)
+ assert_equal(0z07, b_empty)
+ var caught = false
+ try
+ add(b_null, 9)
+ catch /E1131:/
+ caught = true
+ endtry
+ assert_true(caught)
+ enddef
+ TestBlob()
+
+ var d_decl: dict<number>
+ var d_empty = {}
+ var d_null = null_dict
+
+ def TestDict()
+ assert_false(d_decl == null)
+ assert_false(d_decl is null_dict)
+ assert_false(d_empty == null)
+ assert_false(d_empty is null_dict)
+ assert_true(d_null == null)
+ assert_true(d_null is null_dict)
+ assert_true(d_null == null_dict)
+
+ d_decl['a'] = 6
+ assert_equal({a: 6}, d_decl)
+ d_empty['b'] = 7
+ assert_equal({b: 7}, d_empty)
+ var caught = false
+ try
+ d_null['c'] = 9
+ catch /E1103:/
+ caught = true
+ endtry
+ assert_true(caught)
+ enddef
+ TestDict()
+ END
+ v9.CheckScriptSuccess(lines)
+
lines =<< trim END
var d: dict<func> = {f: null_function}
assert_equal(null_function, d.f)
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 4642,
/**/
4641,
/**/
#define ASSIGN_UNPACK 0x10 // using [a, b] = list
#define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type
#define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable
+#define ASSIGN_INIT 0x80 // not assigning a value, just a declaration
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff
* When the value of "sv" is a null list of dict, allocate it.
*/
static void
-allocate_if_null(typval_T *tv)
+allocate_if_null(svar_T *sv)
{
+ typval_T *tv = sv->sv_tv;
+
switch (tv->v_type)
{
case VAR_LIST:
- if (tv->vval.v_list == NULL)
+ if (tv->vval.v_list == NULL && sv->sv_type != &t_list_empty)
(void)rettv_list_alloc(tv);
break;
case VAR_DICT:
- if (tv->vval.v_dict == NULL)
+ if (tv->vval.v_dict == NULL && sv->sv_type != &t_dict_empty)
(void)rettv_dict_alloc(tv);
break;
case VAR_BLOB:
- if (tv->vval.v_blob == NULL)
+ if (tv->vval.v_blob == NULL && sv->sv_type != &t_blob_null)
(void)rettv_blob_alloc(tv);
break;
default:
sv = get_script_svar(sref, ectx->ec_dfunc_idx);
if (sv == NULL)
goto theend;
- allocate_if_null(sv->sv_tv);
+ allocate_if_null(sv);
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend;
copy_tv(sv->sv_tv, STACK_TV_BOT(0));
init_tv.v_type = VAR_NUMBER;
else
init_tv.v_type = type->tt_type;
- set_var_const(name, 0, type, &init_tv, FALSE, 0, 0);
+ set_var_const(name, 0, type, &init_tv, FALSE, ASSIGN_INIT, 0);
vim_free(name);
return p;
if (*type == NULL)
*type = typval2type(tv, get_copyID(), &si->sn_type_list,
do_member ? TVTT_DO_MEMBER : 0);
+ else if ((flags & ASSIGN_INIT) == 0
+ && (*type)->tt_type == VAR_BLOB && tv->v_type == VAR_BLOB
+ && tv->vval.v_blob == NULL)
+ {
+ // "var b: blob = null_blob" has a different type.
+ *type = &t_blob_null;
+ }
if (sv->sv_type_allocated)
free_type(sv->sv_type);
if (*type != NULL && ((*type)->tt_type == VAR_FUNC
if (tv->v_type == VAR_STRING)
return &t_string;
if (tv->v_type == VAR_BLOB)
+ {
+ if (tv->vval.v_blob == NULL)
+ return &t_blob_null;
return &t_blob;
+ }
if (tv->v_type == VAR_LIST)
{