forked from aniani/vim
patch 8.2.4634: Vim9: cannot initialize a variable to null_list
Problem: Vim9: cannot initialize a variable to null_list. Solution: Give negative count to NEWLIST. (closes #10027) Also fix inconsistencies in comparing with null values.
This commit is contained in:
parent
c75bca3ee9
commit
ec15b1cfdc
@ -2816,11 +2816,13 @@ eval_variable(
|
||||
type = sv->sv_type;
|
||||
}
|
||||
|
||||
// If a list or dict variable wasn't initialized, do it now.
|
||||
// Not for global variables, they are not declared.
|
||||
// If a list or dict variable wasn't initialized and has meaningful
|
||||
// type, do it now. Not for global variables, they are not
|
||||
// declared.
|
||||
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)
|
||||
{
|
||||
tv->vval.v_dict = dict_alloc();
|
||||
if (tv->vval.v_dict != NULL)
|
||||
@ -2829,7 +2831,8 @@ eval_variable(
|
||||
tv->vval.v_dict->dv_type = alloc_type(type);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
tv->vval.v_list = list_alloc();
|
||||
if (tv->vval.v_list != NULL)
|
||||
@ -2838,12 +2841,6 @@ eval_variable(
|
||||
tv->vval.v_list->lv_type = alloc_type(type);
|
||||
}
|
||||
}
|
||||
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL)
|
||||
{
|
||||
tv->vval.v_blob = blob_alloc();
|
||||
if (tv->vval.v_blob != NULL)
|
||||
++tv->vval.v_blob->bv_refcount;
|
||||
}
|
||||
}
|
||||
copy_tv(tv, rettv);
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
|
||||
int generate_LOCKCONST(cctx_T *cctx);
|
||||
int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, type_T *type);
|
||||
int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
|
||||
int generate_NEWLIST(cctx_T *cctx, int count);
|
||||
int generate_NEWDICT(cctx_T *cctx, int count);
|
||||
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
|
||||
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
|
||||
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
|
||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
|
||||
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
|
||||
|
@ -122,13 +122,13 @@ def Test_add_blob()
|
||||
END
|
||||
v9.CheckDefExecFailure(lines, 'E1131:', 2)
|
||||
|
||||
# Getting variable with NULL blob allocates a new blob at script level
|
||||
# Getting variable with NULL blob fails
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
var b: blob = test_null_blob()
|
||||
add(b, 123)
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
v9.CheckScriptFailure(lines, 'E1131:', 3)
|
||||
enddef
|
||||
|
||||
def Test_add_list()
|
||||
|
@ -439,11 +439,11 @@ if has('job')
|
||||
'\d\+ STORE $\d\_s*' ..
|
||||
|
||||
'var dd = null_dict\_s*' ..
|
||||
'\d\+ NEWDICT size 0\_s*' ..
|
||||
'\d\+ NEWDICT size -1\_s*' ..
|
||||
'\d\+ STORE $\d\_s*' ..
|
||||
|
||||
'var ll = null_list\_s*' ..
|
||||
'\d\+ NEWLIST size 0\_s*' ..
|
||||
'\d\+ NEWLIST size -1\_s*' ..
|
||||
'\d\+ STORE $\d\_s*' ..
|
||||
|
||||
'var Ff = null_function\_s*' ..
|
||||
|
@ -754,6 +754,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(v:null != test_null_blob())
|
||||
assert_false(null != null_blob)
|
||||
|
||||
var nb = null_blob
|
||||
assert_true(nb == null_blob)
|
||||
assert_true(nb == null)
|
||||
assert_true(null_blob == nb)
|
||||
assert_true(null == nb)
|
||||
|
||||
if has('channel')
|
||||
assert_true(test_null_channel() == v:null)
|
||||
assert_true(null_channel == null)
|
||||
@ -763,6 +769,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(null_channel != null)
|
||||
assert_false(v:null != test_null_channel())
|
||||
assert_false(null != null_channel)
|
||||
|
||||
var nc = null_channel
|
||||
assert_true(nc == null_channel)
|
||||
assert_true(nc == null)
|
||||
assert_true(null_channel == nc)
|
||||
assert_true(null == nc)
|
||||
endif
|
||||
|
||||
assert_true(test_null_dict() == v:null)
|
||||
@ -779,6 +791,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(g:null_dict != v:null)
|
||||
assert_false(v:null != g:null_dict)
|
||||
|
||||
var nd = null_dict
|
||||
assert_true(nd == null_dict)
|
||||
assert_true(nd == null)
|
||||
assert_true(null_dict == nd)
|
||||
assert_true(null == nd)
|
||||
|
||||
assert_true(test_null_function() == v:null)
|
||||
assert_true(null_function == null)
|
||||
assert_true(v:null == test_null_function())
|
||||
@ -788,6 +806,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(v:null != test_null_function())
|
||||
assert_false(null != null_function)
|
||||
|
||||
var Nf = null_function
|
||||
assert_true(Nf == null_function)
|
||||
assert_true(Nf == null)
|
||||
assert_true(null_function == Nf)
|
||||
assert_true(null == Nf)
|
||||
|
||||
if has('job')
|
||||
assert_true(test_null_job() == v:null)
|
||||
assert_true(null_job == null)
|
||||
@ -797,6 +821,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(null_job != null)
|
||||
assert_false(v:null != test_null_job())
|
||||
assert_false(null != null_job)
|
||||
|
||||
var nj = null_job
|
||||
assert_true(nj == null_job)
|
||||
assert_true(nj == null)
|
||||
assert_true(null_job == nj)
|
||||
assert_true(null == nj)
|
||||
endif
|
||||
|
||||
assert_true(test_null_list() == v:null)
|
||||
@ -813,6 +843,12 @@ def Test_expr4_compare_null()
|
||||
assert_true(g:not_null_list != v:null)
|
||||
assert_true(v:null != g:not_null_list)
|
||||
|
||||
var nl = null_list
|
||||
assert_true(nl == null_list)
|
||||
assert_true(nl == null)
|
||||
assert_true(null_list == nl)
|
||||
assert_true(null == nl)
|
||||
|
||||
assert_true(test_null_partial() == v:null)
|
||||
assert_true(null_partial == null)
|
||||
assert_true(v:null == test_null_partial())
|
||||
@ -822,6 +858,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(v:null != test_null_partial())
|
||||
assert_false(null != null_partial)
|
||||
|
||||
var Np = null_partial
|
||||
assert_true(Np == null_partial)
|
||||
assert_true(Np == null)
|
||||
assert_true(null_partial == Np)
|
||||
assert_true(null == Np)
|
||||
|
||||
assert_true(test_null_string() == v:null)
|
||||
assert_true(null_string == null)
|
||||
assert_true(v:null == test_null_string())
|
||||
@ -837,6 +879,12 @@ def Test_expr4_compare_null()
|
||||
assert_false(null_string isnot test_null_string())
|
||||
assert_true(null_string isnot '')
|
||||
assert_true('' isnot null_string)
|
||||
|
||||
var ns = null_string
|
||||
assert_true(ns == null_string)
|
||||
assert_true(ns == null)
|
||||
assert_true(null_string == ns)
|
||||
assert_true(null == ns)
|
||||
END
|
||||
v9.CheckDefAndScriptSuccess(lines)
|
||||
unlet g:null_dict
|
||||
|
15
src/typval.c
15
src/typval.c
@ -1314,6 +1314,19 @@ typval_compare(
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
#ifdef FEAT_JOB_CHANNEL
|
||||
else if (tv1->v_type == tv2->v_type
|
||||
&& (tv1->v_type == VAR_CHANNEL || tv1->v_type == VAR_JOB)
|
||||
&& (type == EXPR_NEQUAL || type == EXPR_EQUAL))
|
||||
{
|
||||
if (tv1->v_type == VAR_CHANNEL)
|
||||
n1 = tv1->vval.v_channel == tv2->vval.v_channel;
|
||||
else
|
||||
n1 = tv1->vval.v_job == tv2->vval.v_job;
|
||||
if (type == EXPR_NEQUAL)
|
||||
n1 = !n1;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
|
||||
@ -1417,7 +1430,7 @@ typval_compare_null(typval_T *tv1, typval_T *tv2)
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
// although comparing null with number, float or bool is not very usefule
|
||||
// although comparing null with number, float or bool is not very useful
|
||||
// we won't give an error
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
4634,
|
||||
/**/
|
||||
4633,
|
||||
/**/
|
||||
|
@ -90,7 +90,9 @@ typedef enum {
|
||||
ISN_PUSHCHANNEL, // push NULL channel
|
||||
ISN_PUSHJOB, // push NULL job
|
||||
ISN_NEWLIST, // push list from stack items, size is isn_arg.number
|
||||
// -1 for null_list
|
||||
ISN_NEWDICT, // push dict from stack items, size is isn_arg.number
|
||||
// -1 for null_dict
|
||||
ISN_NEWPARTIAL, // push NULL partial
|
||||
|
||||
ISN_AUTOLOAD, // get item from autoload import, function or variable
|
||||
|
@ -1955,7 +1955,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
||||
generate_PUSHS(cctx, &li->li_tv.vval.v_string);
|
||||
li->li_tv.vval.v_string = NULL;
|
||||
}
|
||||
generate_NEWLIST(cctx, l->lv_len);
|
||||
generate_NEWLIST(cctx, l->lv_len, FALSE);
|
||||
}
|
||||
list_free(l);
|
||||
p += STRLEN(p);
|
||||
@ -2239,10 +2239,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
||||
generate_PUSHFUNC(cctx, NULL, &t_func_void);
|
||||
break;
|
||||
case VAR_LIST:
|
||||
generate_NEWLIST(cctx, 0);
|
||||
generate_NEWLIST(cctx, 0, FALSE);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
generate_NEWDICT(cctx, 0);
|
||||
generate_NEWDICT(cctx, 0, FALSE);
|
||||
break;
|
||||
case VAR_JOB:
|
||||
generate_PUSHJOB(cctx);
|
||||
|
@ -122,29 +122,103 @@ ufunc_argcount(ufunc_T *ufunc)
|
||||
/*
|
||||
* Create a new list from "count" items at the bottom of the stack.
|
||||
* When "count" is zero an empty list is added to the stack.
|
||||
* When "count" is -1 a NULL list is added to the stack.
|
||||
*/
|
||||
static int
|
||||
exe_newlist(int count, ectx_T *ectx)
|
||||
{
|
||||
list_T *list = list_alloc_with_items(count);
|
||||
list_T *list = NULL;
|
||||
int idx;
|
||||
typval_T *tv;
|
||||
|
||||
if (list == NULL)
|
||||
return FAIL;
|
||||
for (idx = 0; idx < count; ++idx)
|
||||
list_set_item(list, idx, STACK_TV_BOT(idx - count));
|
||||
if (count >= 0)
|
||||
{
|
||||
list = list_alloc_with_items(count);
|
||||
if (list == NULL)
|
||||
return FAIL;
|
||||
for (idx = 0; idx < count; ++idx)
|
||||
list_set_item(list, idx, STACK_TV_BOT(idx - count));
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
ectx->ec_stack.ga_len -= count - 1;
|
||||
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
{
|
||||
list_unref(list);
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
++ectx->ec_stack.ga_len;
|
||||
tv = STACK_TV_BOT(-1);
|
||||
tv->v_type = VAR_LIST;
|
||||
tv->vval.v_list = list;
|
||||
++list->lv_refcount;
|
||||
if (list != NULL)
|
||||
++list->lv_refcount;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of ISN_NEWDICT.
|
||||
* Returns FAIL on total failure, MAYBE on error.
|
||||
*/
|
||||
static int
|
||||
exe_newdict(int count, ectx_T *ectx)
|
||||
{
|
||||
dict_T *dict = NULL;
|
||||
dictitem_T *item;
|
||||
char_u *key;
|
||||
int idx;
|
||||
typval_T *tv;
|
||||
|
||||
if (count >= 0)
|
||||
{
|
||||
dict = dict_alloc();
|
||||
if (unlikely(dict == NULL))
|
||||
return FAIL;
|
||||
for (idx = 0; idx < count; ++idx)
|
||||
{
|
||||
// have already checked key type is VAR_STRING
|
||||
tv = STACK_TV_BOT(2 * (idx - count));
|
||||
// check key is unique
|
||||
key = tv->vval.v_string == NULL
|
||||
? (char_u *)"" : tv->vval.v_string;
|
||||
item = dict_find(dict, key, -1);
|
||||
if (item != NULL)
|
||||
{
|
||||
semsg(_(e_duplicate_key_in_dicitonary), key);
|
||||
dict_unref(dict);
|
||||
return MAYBE;
|
||||
}
|
||||
item = dictitem_alloc(key);
|
||||
clear_tv(tv);
|
||||
if (unlikely(item == NULL))
|
||||
{
|
||||
dict_unref(dict);
|
||||
return FAIL;
|
||||
}
|
||||
item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
|
||||
item->di_tv.v_lock = 0;
|
||||
if (dict_add(dict, item) == FAIL)
|
||||
{
|
||||
// can this ever happen?
|
||||
dict_unref(dict);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
ectx->ec_stack.ga_len -= 2 * count - 1;
|
||||
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
return FAIL;
|
||||
else
|
||||
++ectx->ec_stack.ga_len;
|
||||
tv = STACK_TV_BOT(-1);
|
||||
tv->v_type = VAR_DICT;
|
||||
tv->v_lock = 0;
|
||||
tv->vval.v_dict = dict;
|
||||
if (dict != NULL)
|
||||
++dict->dv_refcount;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -3357,57 +3431,14 @@ exec_instructions(ectx_T *ectx)
|
||||
// create a dict from items on the stack
|
||||
case ISN_NEWDICT:
|
||||
{
|
||||
int count = iptr->isn_arg.number;
|
||||
dict_T *dict = dict_alloc();
|
||||
dictitem_T *item;
|
||||
char_u *key;
|
||||
int idx;
|
||||
int res;
|
||||
|
||||
if (unlikely(dict == NULL))
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
res = exe_newdict(iptr->isn_arg.number, ectx);
|
||||
if (res == FAIL)
|
||||
goto theend;
|
||||
for (idx = 0; idx < count; ++idx)
|
||||
{
|
||||
// have already checked key type is VAR_STRING
|
||||
tv = STACK_TV_BOT(2 * (idx - count));
|
||||
// check key is unique
|
||||
key = tv->vval.v_string == NULL
|
||||
? (char_u *)"" : tv->vval.v_string;
|
||||
item = dict_find(dict, key, -1);
|
||||
if (item != NULL)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
semsg(_(e_duplicate_key_in_dicitonary), key);
|
||||
dict_unref(dict);
|
||||
goto on_error;
|
||||
}
|
||||
item = dictitem_alloc(key);
|
||||
clear_tv(tv);
|
||||
if (unlikely(item == NULL))
|
||||
{
|
||||
dict_unref(dict);
|
||||
goto theend;
|
||||
}
|
||||
item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
|
||||
item->di_tv.v_lock = 0;
|
||||
if (dict_add(dict, item) == FAIL)
|
||||
{
|
||||
// can this ever happen?
|
||||
dict_unref(dict);
|
||||
goto theend;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
ectx->ec_stack.ga_len -= 2 * count - 1;
|
||||
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
goto theend;
|
||||
else
|
||||
++ectx->ec_stack.ga_len;
|
||||
tv = STACK_TV_BOT(-1);
|
||||
tv->v_type = VAR_DICT;
|
||||
tv->v_lock = 0;
|
||||
tv->vval.v_dict = dict;
|
||||
++dict->dv_refcount;
|
||||
if (res == MAYBE)
|
||||
goto on_error;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -958,7 +958,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
*arg = p;
|
||||
|
||||
ppconst->pp_is_const = is_all_const;
|
||||
return generate_NEWLIST(cctx, count);
|
||||
return generate_NEWLIST(cctx, count, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1246,7 +1246,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
||||
|
||||
dict_unref(d);
|
||||
ppconst->pp_is_const = is_all_const;
|
||||
return generate_NEWDICT(cctx, count);
|
||||
return generate_NEWDICT(cctx, count, FALSE);
|
||||
|
||||
failret:
|
||||
if (*arg == NULL)
|
||||
|
@ -581,12 +581,12 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
|
||||
case VAR_LIST:
|
||||
if (tv->vval.v_list != NULL)
|
||||
iemsg("non-empty list constant not supported");
|
||||
generate_NEWLIST(cctx, 0);
|
||||
generate_NEWLIST(cctx, 0, TRUE);
|
||||
break;
|
||||
case VAR_DICT:
|
||||
if (tv->vval.v_dict != NULL)
|
||||
iemsg("non-empty dict constant not supported");
|
||||
generate_NEWDICT(cctx, 0);
|
||||
generate_NEWDICT(cctx, 0, TRUE);
|
||||
break;
|
||||
#ifdef FEAT_JOB_CHANNEL
|
||||
case VAR_JOB:
|
||||
@ -1115,10 +1115,11 @@ generate_VIM9SCRIPT(
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_NEWLIST instruction.
|
||||
* Generate an ISN_NEWLIST instruction for "count" items.
|
||||
* "use_null" is TRUE for null_list.
|
||||
*/
|
||||
int
|
||||
generate_NEWLIST(cctx_T *cctx, int count)
|
||||
generate_NEWLIST(cctx_T *cctx, int count, int use_null)
|
||||
{
|
||||
isn_T *isn;
|
||||
type_T *member_type;
|
||||
@ -1128,7 +1129,7 @@ generate_NEWLIST(cctx_T *cctx, int count)
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_NEWLIST)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.number = count;
|
||||
isn->isn_arg.number = use_null ? -1 : count;
|
||||
|
||||
// Get the member type and the declared member type from all the items on
|
||||
// the stack.
|
||||
@ -1145,9 +1146,10 @@ generate_NEWLIST(cctx_T *cctx, int count)
|
||||
|
||||
/*
|
||||
* Generate an ISN_NEWDICT instruction.
|
||||
* "use_null" is TRUE for null_dict.
|
||||
*/
|
||||
int
|
||||
generate_NEWDICT(cctx_T *cctx, int count)
|
||||
generate_NEWDICT(cctx_T *cctx, int count, int use_null)
|
||||
{
|
||||
isn_T *isn;
|
||||
type_T *member_type;
|
||||
@ -1157,7 +1159,7 @@ generate_NEWDICT(cctx_T *cctx, int count)
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_NEWDICT)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.number = count;
|
||||
isn->isn_arg.number = use_null ? -1 : count;
|
||||
|
||||
member_type = get_member_type_from_stack(count, 2, cctx);
|
||||
type = get_dict_type(member_type, cctx->ctx_type_list);
|
||||
|
Loading…
x
Reference in New Issue
Block a user