]> granicus.if.org Git - vim/commitdiff
patch 8.2.4634: Vim9: cannot initialize a variable to null_list v8.2.4634
authorBram Moolenaar <Bram@vim.org>
Sun, 27 Mar 2022 15:29:53 +0000 (16:29 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 27 Mar 2022 15:29:53 +0000 (16:29 +0100)
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.

12 files changed:
src/evalvars.c
src/proto/vim9instr.pro
src/testdir/test_vim9_builtin.vim
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/typval.c
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index 058e8048f6e3832ece75c870fd904fc5fed70ae4..19a0f33894743538816506c188aaab3c44ed692a 100644 (file)
@@ -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);
        }
index 76f3b2102954ea5bb65b17df0575a51d768dde91..52a7c79f1964adf99458f56bc6a0913dd2fe7d1c 100644 (file)
@@ -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);
index 8b9dbd605123e9083fb37a940bc29e597f50ba22..24a7f9583e7aba4a6151d1805f321a28ae2b46bb 100644 (file)
@@ -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()
index 77c4675888ef6a0280d8949ed67af9f7d7d4fb75..7e3021333cfded22126e47b90e02801348ba8ced 100644 (file)
@@ -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*' ..
index 41e398aea42c5af090d6a8196f91aaac2040102b..209bb10a5eebbdb8098348ee691b9c8b3823b65b 100644 (file)
@@ -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
index d0c937e5d1e9b376aa1ee70590a6145efc48f7fc..50b1d620a6d3f882506cf548a06849510298c138 100644 (file)
@@ -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;
 }
index 169468b0ec1ff96d0db8464af5bd2f3f13de1886..6986aaa571aa56a8dc6e51c0f6769b11460802be 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4634,
 /**/
     4633,
 /**/
index ed5f6769594a4f4fe96d9b447499c4a5c6608fdd..60c54d9f55a156716a2250a7a85c8f76a25bb645 100644 (file)
@@ -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
index 1eb6ce22fc3173c984a48769e5bb2d7156af4e8a..4e3a1bfb1f556a41613e2f3b8251b001bef253d9 100644 (file)
@@ -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);
index 2d6a3feadd54d2c4b3d891ed32d8b1fc2c2782f0..0404eb4123739c4df8d6b9e4ad380e061aa935f0 100644 (file)
@@ -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;
 
index 8c7d0b0e9538357e2ad40524210fcb765825b046..90d7c8cf4a530971628947892ad4c10af8e2a468 100644 (file)
@@ -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)
index f0206211d80cbf5604f00e23563f85fb3f845627..57943b8a5a3581958211c508b425a66016145408 100644 (file)
@@ -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);