summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-03-27 16:29:53 +0100
committerBram Moolenaar <Bram@vim.org>2022-03-27 16:29:53 +0100
commitec15b1cfdc5faadb529dedda58adf7fc98c839ed (patch)
treecdcf7ffa3e2ed7de3fc890f6415a0be84ad1e490
parentc75bca3ee955ff36ece99a42041733ddea5f45a7 (diff)
downloadvim-git-ec15b1cfdc5faadb529dedda58adf7fc98c839ed.tar.gz
patch 8.2.4634: Vim9: cannot initialize a variable to null_listv8.2.4634
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.
-rw-r--r--src/evalvars.c17
-rw-r--r--src/proto/vim9instr.pro4
-rw-r--r--src/testdir/test_vim9_builtin.vim4
-rw-r--r--src/testdir/test_vim9_disassemble.vim4
-rw-r--r--src/testdir/test_vim9_expr.vim48
-rw-r--r--src/typval.c15
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h2
-rw-r--r--src/vim9compile.c6
-rw-r--r--src/vim9execute.c141
-rw-r--r--src/vim9expr.c4
-rw-r--r--src/vim9instr.c16
12 files changed, 179 insertions, 84 deletions
diff --git a/src/evalvars.c b/src/evalvars.c
index 058e8048f..19a0f3389 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -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);
}
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 76f3b2102..52a7c79f1 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -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);
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 8b9dbd605..24a7f9583 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -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()
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 77c467588..7e3021333 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -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*' ..
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 41e398aea..209bb10a5 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -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
diff --git a/src/typval.c b/src/typval.c
index d0c937e5d..50b1d620a 100644
--- a/src/typval.c
+++ b/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;
}
diff --git a/src/version.c b/src/version.c
index 169468b0e..6986aaa57 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4634,
+/**/
4633,
/**/
4632,
diff --git a/src/vim9.h b/src/vim9.h
index ed5f67695..60c54d9f5 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -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
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 1eb6ce22f..4e3a1bfb1 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -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);
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 2d6a3fead..0404eb412 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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;
-
- if (unlikely(dict == NULL))
- 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;
- }
- }
+ int res;
- if (count > 0)
- ectx->ec_stack.ga_len -= 2 * count - 1;
- else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+ SOURCING_LNUM = iptr->isn_lnum;
+ res = exe_newdict(iptr->isn_arg.number, ectx);
+ if (res == FAIL)
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;
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 8c7d0b0e9..90d7c8cf4 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -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)
diff --git a/src/vim9instr.c b/src/vim9instr.c
index f0206211d..57943b8a5 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -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);