]> granicus.if.org Git - vim/commitdiff
patch 8.2.4487: Vim9: cannot compare with v:null v8.2.4487
authorBram Moolenaar <Bram@vim.org>
Tue, 1 Mar 2022 19:23:24 +0000 (19:23 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 1 Mar 2022 19:23:24 +0000 (19:23 +0000)
Problem:    Vim9: cannot compare with v:null.
Solution:   Allow comparing anything with v:null. (closes #9866)

src/evalvars.c
src/proto/typval.pro
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/typval.c
src/version.c
src/vim9.h
src/vim9execute.c
src/vim9instr.c

index 96d60a9595ba9c4cfee0aa8b46e72bd2ffdca701..862d5192bb4e5fbc9cf386ae2b8c2033098014aa 100644 (file)
@@ -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);
        }
     }
index f04bf1539ad872d6afc5d9c88cbb6eded60c1d2e..b744bbcad13d0801afc2979920271cce1ba9134d 100644 (file)
@@ -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);
index 91464a7687a22421722a579c08f5df9fe594223e..028e1a760ec5f3070bc8e18a9014859d1f12350a 100644 (file)
@@ -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 !='],
index 4aad9c98c9185cb2d71b689d3d7829f0d00eb321..a38254f7dc5856382d57d0df2f87557574bbe07a 100644 (file)
@@ -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([
index ab226403dfa08e5e5da1b6dff74e4973a8b670f1..775466784d17ef1447806649f2bfb61d35f6d572 100644 (file)
@@ -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)
@@ -1365,6 +1380,35 @@ typval_compare_list(
     return OK;
 }
 
+/*
+ * 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".
index e563142a164418bf452376af3be301bbbe758ee4..960e18cea8fd7c602f6e2fbc9e43285a64b16c6f 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4487,
 /**/
     4486,
 /**/
index 87ee6b163f1677655b140c63ea1dc8ad5a657ba4..07dd6392f3120c69d7e55b5e6408867ed21ec3a3 100644 (file)
@@ -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,
index a7b76c639e15386347c4be3f00c99e2ef13061f8..56220f862e7ab6052a71eba8ee4ed8a4b5264f37 100644 (file)
@@ -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:
index 9116f4111c6341481d958920ade121f4e18f2ce5..2c76fadc3acb7414dafb9ad9511f3e679aa03f4a 100644 (file)
@@ -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: