Patch 8.2.4487
Problem:    Vim9: cannot compare with v:null.
Solution:   Allow comparing anything with v:null. (closes #9866)
Files:      src/vim9instr.c, src/typval.c, src/proto/typval.pro,
            src/vim9.h, src/vim9execute.c, src/evalvars.c,
            src/testdir/test_vim9_expr.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.4486/src/vim9instr.c     2022-02-06 17:16:57.730598839 +0000
--- src/vim9instr.c     2022-03-01 17:51:00.890904474 +0000
***************
*** 372,377 ****
--- 372,395 ----
            || ((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,394 ****
                    && (type1 == VAR_BOOL || type1 == VAR_SPECIAL
                       || type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
            || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
!                                && exprtype != EXPR_IS && exprtype != 
EXPR_ISNOT
                    && (type1 == VAR_BLOB || type2 == VAR_BLOB
                        || type1 == VAR_LIST || type2 == VAR_LIST))))
      {
--- 406,412 ----
                    && (type1 == VAR_BOOL || type1 == VAR_SPECIAL
                       || type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
            || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
!                              && exprtype != EXPR_IS && exprtype != EXPR_ISNOT
                    && (type1 == VAR_BLOB || type2 == VAR_BLOB
                        || type1 == VAR_LIST || type2 == VAR_LIST))))
      {
***************
*** 2131,2136 ****
--- 2149,2155 ----
        case ISN_COMPAREFUNC:
        case ISN_COMPARELIST:
        case ISN_COMPARENR:
+       case ISN_COMPARENULL:
        case ISN_COMPARESPECIAL:
        case ISN_COMPARESTRING:
        case ISN_CONCAT:
*** ../vim-8.2.4486/src/typval.c        2022-01-08 21:38:48.203970853 +0000
--- src/typval.c        2022-03-01 19:16:46.253253162 +0000
***************
*** 1169,1174 ****
--- 1169,1189 ----
        // 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,1371 ****
--- 1381,1415 ----
  }
  
  /*
+  * 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.
*** ../vim-8.2.4486/src/proto/typval.pro        2021-12-25 19:29:18.409881900 
+0000
--- src/proto/typval.pro        2022-03-01 17:49:39.059010347 +0000
***************
*** 57,62 ****
--- 57,63 ----
  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);
*** ../vim-8.2.4486/src/vim9.h  2022-02-13 21:51:02.388484128 +0000
--- src/vim9.h  2022-03-01 17:51:30.846865392 +0000
***************
*** 134,139 ****
--- 134,140 ----
      // 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,
*** ../vim-8.2.4486/src/vim9execute.c   2022-02-28 20:54:58.129239044 +0000
--- src/vim9execute.c   2022-03-01 17:50:40.442931042 +0000
***************
*** 3880,3885 ****
--- 3880,3904 ----
                }
                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,5906 ****
--- 5920,5926 ----
  
            case ISN_COMPAREBOOL:
            case ISN_COMPARESPECIAL:
+           case ISN_COMPARENULL:
            case ISN_COMPARENR:
            case ISN_COMPAREFLOAT:
            case ISN_COMPARESTRING:
***************
*** 5936,5941 ****
--- 5956,5962 ----
                           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:
*** ../vim-8.2.4486/src/evalvars.c      2022-02-21 15:59:07.571981059 +0000
--- src/evalvars.c      2022-03-01 18:23:56.311653905 +0000
***************
*** 2816,2844 ****
            }
  
            // If a list or dict variable wasn't initialized, do it now.
!           if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
            {
!               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)
                {
!                   ++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_blob = blob_alloc();
-               if (tv->vval.v_blob != NULL)
-                   ++tv->vval.v_blob->bv_refcount;
            }
            copy_tv(tv, rettv);
        }
--- 2816,2848 ----
            }
  
            // If a list or dict variable wasn't initialized, 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)
                {
!                   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)
!                   {
!                       ++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_blob = blob_alloc();
!                   if (tv->vval.v_blob != NULL)
!                       ++tv->vval.v_blob->bv_refcount;
                }
            }
            copy_tv(tv, rettv);
        }
*** ../vim-8.2.4486/src/testdir/test_vim9_expr.vim      2022-02-28 
20:54:58.129239044 +0000
--- src/testdir/test_vim9_expr.vim      2022-03-01 19:22:20.004489377 +0000
***************
*** 712,717 ****
--- 712,792 ----
    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([
*** ../vim-8.2.4486/src/testdir/test_vim9_disassemble.vim       2022-02-15 
15:37:07.319841654 +0000
--- src/testdir/test_vim9_disassemble.vim       2022-03-01 18:34:11.790599653 
+0000
***************
*** 1846,1851 ****
--- 1846,1853 ----
          ['true != isFalse', 'COMPAREBOOL !='],
          ['v:none == isNull', 'COMPARESPECIAL =='],
          ['v:none != isNull', 'COMPARESPECIAL !='],
+         ['"text" == isNull', 'COMPARENULL =='],
+         ['"text" != isNull', 'COMPARENULL !='],
  
          ['111 == aNumber', 'COMPARENR =='],
          ['111 != aNumber', 'COMPARENR !='],
*** ../vim-8.2.4486/src/version.c       2022-03-01 16:00:03.131398059 +0000
--- src/version.c       2022-03-01 17:28:24.576164421 +0000
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     4487,
  /**/

-- 
Birthdays are healthy.  The more you have them, the longer you live.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220301192351.D5DF51C1769%40moolenaar.net.

Raspunde prin e-mail lui