summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-03-01 19:23:24 +0000
committerBram Moolenaar <Bram@vim.org>2022-03-01 19:23:24 +0000
commit7a2222487507eb13cccdb9a66397092775d62b8c (patch)
treec5878917461a22b48ec7a38cb6356352b8e57895
parentf01af9c4e6f1438cd1112cfff42f3837028c7846 (diff)
patch 8.2.4487: Vim9: cannot compare with v:nullv8.2.4487
Problem: Vim9: cannot compare with v:null. Solution: Allow comparing anything with v:null. (closes #9866)
-rw-r--r--src/evalvars.c40
-rw-r--r--src/proto/typval.pro1
-rw-r--r--src/testdir/test_vim9_disassemble.vim2
-rw-r--r--src/testdir/test_vim9_expr.vim75
-rw-r--r--src/typval.c44
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9execute.c21
-rw-r--r--src/vim9instr.c21
9 files changed, 188 insertions, 19 deletions
diff --git a/src/evalvars.c b/src/evalvars.c
index 96d60a9595..862d5192bb 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2816,30 +2816,34 @@ eval_variable(
}
// If a list or dict variable wasn't initialized, do it now.
- if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
+ // Not for global variables, they are not declared.
+ if (ht != &globvarht)
{
- tv->vval.v_dict = dict_alloc();
- if (tv->vval.v_dict != NULL)
+ if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
{
- ++tv->vval.v_dict->dv_refcount;
- tv->vval.v_dict->dv_type = alloc_type(type);
+ tv->vval.v_dict = dict_alloc();
+ if (tv->vval.v_dict != NULL)
+ {
+ ++tv->vval.v_dict->dv_refcount;
+ tv->vval.v_dict->dv_type = alloc_type(type);
+ }
}
- }
- else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL)
- {
- tv->vval.v_list = list_alloc();
- if (tv->vval.v_list != NULL)
+ else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL)
+ {
+ tv->vval.v_list = list_alloc();
+ if (tv->vval.v_list != NULL)
+ {
+ ++tv->vval.v_list->lv_refcount;
+ tv->vval.v_list->lv_type = alloc_type(type);
+ }
+ }
+ else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL)
{
- ++tv->vval.v_list->lv_refcount;
- tv->vval.v_list->lv_type = alloc_type(type);
+ tv->vval.v_blob = blob_alloc();
+ if (tv->vval.v_blob != NULL)
+ ++tv->vval.v_blob->bv_refcount;
}
}
- 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/typval.pro b/src/proto/typval.pro
index f04bf1539a..b744bbcad1 100644
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -57,6 +57,7 @@ int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
void copy_tv(typval_T *from, typval_T *to);
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
+int typval_compare_null(typval_T *tv1, typval_T *tv2);
int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 91464a7687..028e1a760e 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -1846,6 +1846,8 @@ def Test_disassemble_compare()
['true != isFalse', 'COMPAREBOOL !='],
['v:none == isNull', 'COMPARESPECIAL =='],
['v:none != isNull', 'COMPARESPECIAL !='],
+ ['"text" == isNull', 'COMPARENULL =='],
+ ['"text" != isNull', 'COMPARENULL !='],
['111 == aNumber', 'COMPARENR =='],
['111 != aNumber', 'COMPARENR !='],
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 4aad9c98c9..a38254f7dc 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -712,6 +712,81 @@ def Test_expr4_equal()
unlet g:notReached
enddef
+def Test_expr4_compare_null()
+ g:null_dict = test_null_dict()
+ g:not_null_list = []
+ var lines =<< trim END
+ assert_true(test_null_blob() == v:null)
+ assert_true(v:null == test_null_blob())
+ assert_false(test_null_blob() != v:null)
+ assert_false(v:null != test_null_blob())
+
+ if has('channel')
+ assert_true(test_null_channel() == v:null)
+ assert_true(v:null == test_null_channel())
+ assert_false(test_null_channel() != v:null)
+ assert_false(v:null != test_null_channel())
+ endif
+
+ assert_true(test_null_dict() == v:null)
+ assert_true(v:null == test_null_dict())
+ assert_false(test_null_dict() != v:null)
+ assert_false(v:null != test_null_dict())
+
+ assert_true(g:null_dict == v:null)
+ assert_true(v:null == g:null_dict)
+ assert_false(g:null_dict != v:null)
+ assert_false(v:null != g:null_dict)
+
+ assert_true(test_null_function() == v:null)
+ assert_true(v:null == test_null_function())
+ assert_false(test_null_function() != v:null)
+ assert_false(v:null != test_null_function())
+
+ if has('job')
+ assert_true(test_null_job() == v:null)
+ assert_true(v:null == test_null_job())
+ assert_false(test_null_job() != v:null)
+ assert_false(v:null != test_null_job())
+ endif
+
+ assert_true(test_null_list() == v:null)
+ assert_true(v:null == test_null_list())
+ assert_false(test_null_list() != v:null)
+ assert_false(v:null != test_null_list())
+
+ assert_false(g:not_null_list == v:null)
+ assert_false(v:null == g:not_null_list)
+ assert_true(g:not_null_list != v:null)
+ assert_true(v:null != g:not_null_list)
+
+ assert_true(test_null_partial() == v:null)
+ assert_true(v:null == test_null_partial())
+ assert_false(test_null_partial() != v:null)
+ assert_false(v:null != test_null_partial())
+
+ assert_true(test_null_string() == v:null)
+ assert_true(v:null == test_null_string())
+ assert_false(test_null_string() != v:null)
+ assert_false(v:null != test_null_string())
+ END
+ v9.CheckDefAndScriptSuccess(lines)
+ unlet g:null_dict
+ unlet g:not_null_list
+
+ v9.CheckDefAndScriptFailure(['echo 123 == v:null'], 'E1072: Cannot compare number with special')
+ v9.CheckDefAndScriptFailure(['echo v:null == 123'], 'E1072: Cannot compare special with number')
+ v9.CheckDefAndScriptFailure(['echo 123 != v:null'], 'E1072: Cannot compare number with special')
+ v9.CheckDefAndScriptFailure(['echo v:null != 123'], 'E1072: Cannot compare special with number')
+ v9.CheckDefAndScriptFailure(['echo true == v:null'], 'E1072: Cannot compare bool with special')
+ v9.CheckDefAndScriptFailure(['echo v:null == true'], 'E1072: Cannot compare special with bool')
+ v9.CheckDefAndScriptFailure(['echo true != v:null'], 'E1072: Cannot compare bool with special')
+ v9.CheckDefAndScriptFailure(['echo v:null != true'], 'E1072: Cannot compare special with bool')
+ v9.CheckDefAndScriptFailure(['echo false == v:null'], 'E1072: Cannot compare bool with special')
+
+ v9.CheckDefExecAndScriptFailure(['echo [] == v:none'], ['E1072: Cannot compare list with special', 'E691: Can only compare List with List'])
+enddef
+
def Test_expr4_wrong_type()
for op in ['>', '>=', '<', '<=', '=~', '!~']
v9.CheckDefExecAndScriptFailure([
diff --git a/src/typval.c b/src/typval.c
index ab226403df..775466784d 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -1169,6 +1169,21 @@ typval_compare(
// it means TRUE.
n1 = (type == EXPR_ISNOT);
}
+ else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
+ || (tv2->v_type == VAR_SPECIAL
+ && tv2->vval.v_number == VVAL_NULL))
+ && tv1->v_type != tv2->v_type
+ && (type == EXPR_EQUAL || type == EXPR_NEQUAL))
+ {
+ n1 = typval_compare_null(tv1, tv2);
+ if (n1 == MAYBE)
+ {
+ clear_tv(tv1);
+ return FAIL;
+ }
+ if (type == EXPR_NEQUAL)
+ n1 = !n1;
+ }
else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
{
if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
@@ -1366,6 +1381,35 @@ typval_compare_list(
}
/*
+ * Compare v:null/v:none with another type. Return TRUE if the value is NULL.
+ */
+ int
+typval_compare_null(typval_T *tv1, typval_T *tv2)
+{
+ if ((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
+ || (tv2->v_type == VAR_SPECIAL && tv2->vval.v_number == VVAL_NULL))
+ {
+ typval_T *tv = tv1->v_type == VAR_SPECIAL ? tv2 : tv1;
+
+ switch (tv->v_type)
+ {
+ case VAR_BLOB: return tv->vval.v_blob == NULL;
+ case VAR_CHANNEL: return tv->vval.v_channel == NULL;
+ case VAR_DICT: return tv->vval.v_dict == NULL;
+ case VAR_FUNC: return tv->vval.v_string == NULL;
+ case VAR_JOB: return tv->vval.v_job == NULL;
+ case VAR_LIST: return tv->vval.v_list == NULL;
+ case VAR_PARTIAL: return tv->vval.v_partial == NULL;
+ case VAR_STRING: return tv->vval.v_string == NULL;
+ default: break;
+ }
+ }
+ semsg(_(e_cannot_compare_str_with_str),
+ vartype_name(tv1->v_type), vartype_name(tv2->v_type));
+ return MAYBE;
+}
+
+/*
* Compare "tv1" to "tv2" as blobs acording to "type".
* Put the result, false or true, in "res".
* Return FAIL and give an error message when the comparison can't be done.
diff --git a/src/version.c b/src/version.c
index e563142a16..960e18cea8 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4487,
+/**/
4486,
/**/
4485,
diff --git a/src/vim9.h b/src/vim9.h
index 87ee6b163f..07dd6392f3 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -134,6 +134,7 @@ typedef enum {
// comparative operations; isn_arg.op.op_type is exprtype_T, op_ic used
ISN_COMPAREBOOL,
ISN_COMPARESPECIAL,
+ ISN_COMPARENULL,
ISN_COMPARENR,
ISN_COMPAREFLOAT,
ISN_COMPARESTRING,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index a7b76c639e..56220f862e 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3880,6 +3880,25 @@ exec_instructions(ectx_T *ectx)
}
break;
+ case ISN_COMPARENULL:
+ {
+ typval_T *tv1 = STACK_TV_BOT(-2);
+ typval_T *tv2 = STACK_TV_BOT(-1);
+ int res;
+
+ res = typval_compare_null(tv1, tv2);
+ if (res == MAYBE)
+ goto on_error;
+ if (iptr->isn_arg.op.op_type == EXPR_NEQUAL)
+ res = !res;
+ clear_tv(tv1);
+ clear_tv(tv2);
+ --ectx->ec_stack.ga_len;
+ tv1->v_type = VAR_BOOL;
+ tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
+ }
+ break;
+
// Operation with two number arguments
case ISN_OPNR:
case ISN_COMPARENR:
@@ -5901,6 +5920,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_COMPAREBOOL:
case ISN_COMPARESPECIAL:
+ case ISN_COMPARENULL:
case ISN_COMPARENR:
case ISN_COMPAREFLOAT:
case ISN_COMPARESTRING:
@@ -5936,6 +5956,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_COMPAREBOOL: type = "COMPAREBOOL"; break;
case ISN_COMPARESPECIAL:
type = "COMPARESPECIAL"; break;
+ case ISN_COMPARENULL: type = "COMPARENULL"; break;
case ISN_COMPARENR: type = "COMPARENR"; break;
case ISN_COMPAREFLOAT: type = "COMPAREFLOAT"; break;
case ISN_COMPARESTRING:
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 9116f4111c..2c76fadc3a 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -372,6 +372,24 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
|| ((type1 == VAR_NUMBER || type1 == VAR_FLOAT)
&& (type2 == VAR_NUMBER || type2 == VAR_FLOAT)))
isntype = ISN_COMPAREANY;
+ else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL)
+ {
+ switch (type1 == VAR_SPECIAL ? type2 : type1)
+ {
+ case VAR_BLOB: break;
+ case VAR_CHANNEL: break;
+ case VAR_DICT: break;
+ case VAR_FUNC: break;
+ case VAR_JOB: break;
+ case VAR_LIST: break;
+ case VAR_PARTIAL: break;
+ case VAR_STRING: break;
+ default: semsg(_(e_cannot_compare_str_with_str),
+ vartype_name(type1), vartype_name(type2));
+ return ISN_DROP;
+ }
+ isntype = ISN_COMPARENULL;
+ }
if ((exprtype == EXPR_IS || exprtype == EXPR_ISNOT)
&& (isntype == ISN_COMPAREBOOL
@@ -388,7 +406,7 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
&& (type1 == VAR_BOOL || type1 == VAR_SPECIAL
|| type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
- && exprtype != EXPR_IS && exprtype != EXPR_ISNOT
+ && exprtype != EXPR_IS && exprtype != EXPR_ISNOT
&& (type1 == VAR_BLOB || type2 == VAR_BLOB
|| type1 == VAR_LIST || type2 == VAR_LIST))))
{
@@ -2131,6 +2149,7 @@ delete_instr(isn_T *isn)
case ISN_COMPAREFUNC:
case ISN_COMPARELIST:
case ISN_COMPARENR:
+ case ISN_COMPARENULL:
case ISN_COMPARESPECIAL:
case ISN_COMPARESTRING:
case ISN_CONCAT: