]> granicus.if.org Git - vim/commitdiff
patch 8.2.4642: Vim9: in :def function script var cannot be null v8.2.4642
authorBram Moolenaar <Bram@vim.org>
Mon, 28 Mar 2022 14:22:35 +0000 (15:22 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 28 Mar 2022 14:22:35 +0000 (15:22 +0100)
Problem:    Vim9: in :def function script var cannot be null.
Solution:   Only initialize a script variable when not set to a null value.
            (closes #10034)

src/evalvars.c
src/globals.h
src/testdir/test_vim9_expr.vim
src/version.c
src/vim.h
src/vim9execute.c
src/vim9script.c
src/vim9type.c

index b49178f125f5e190c136d5cfc81e79646ac5e7bd..202ada1914f84477e700056d4cf11ed3bebae0d9 100644 (file)
@@ -2823,7 +2823,7 @@ eval_variable(
            {
                if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
                          && ((type != NULL && type != &t_dict_empty)
-                                                          || !in_vim9script()))
+                                                         || !in_vim9script()))
                {
                    tv->vval.v_dict = dict_alloc();
                    if (tv->vval.v_dict != NULL)
@@ -2843,6 +2843,14 @@ eval_variable(
                        tv->vval.v_list->lv_type = alloc_type(type);
                    }
                }
+               else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
+                                   && ((type != NULL && type != &t_blob_null)
+                                                         || !in_vim9script()))
+               {
+                   tv->vval.v_blob = blob_alloc();
+                   if (tv->vval.v_blob != NULL)
+                       ++tv->vval.v_blob->bv_refcount;
+               }
            }
            copy_tv(tv, rettv);
        }
index 1319b1f4b0e716806f43c5af18ffb7a59ca6b167..f690efffc4d5481e7d6373df76200aa4ebf8903a 100644 (file)
@@ -405,6 +405,7 @@ EXTERN type_T t_number_bool INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK
 EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
+EXTERN type_T t_blob_null INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, &t_void, NULL);
 EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
 EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
 
index 209bb10a5eebbdb8098348ee691b9c8b3823b65b..e76e1344af74f85aaef68c4c5d13708e1d12323e 100644 (file)
@@ -890,6 +890,93 @@ def Test_expr4_compare_null()
   unlet g:null_dict
   unlet g:not_null_list
 
+  # variables declared at script level used in a :def function
+  lines =<< trim END
+      vim9script
+      
+      var l_decl: list<number>
+      var l_empty = []
+      var l_null = null_list
+
+      def TestList()
+        assert_false(l_decl == null)
+        assert_false(l_decl is null_list)
+        assert_false(l_empty == null)
+        assert_false(l_empty is null_list)
+        assert_true(l_null == null)
+        assert_true(l_null is null_list)
+        assert_true(l_null == null_list)
+
+        add(l_decl, 6)
+        assert_equal([6], l_decl)
+        add(l_empty, 7)
+        assert_equal([7], l_empty)
+        var caught = false
+        try
+          add(l_null, 9)
+        catch /E1130:/
+          caught = true
+        endtry
+        assert_true(caught)
+      enddef
+      TestList()
+      
+      var b_decl: blob
+      var b_empty = 0z
+      var b_null = null_blob
+
+      def TestBlob()
+        assert_false(b_decl == null)
+        assert_false(b_decl is null_blob)
+        assert_false(b_empty == null)
+        assert_false(b_empty is null_blob)
+        assert_true(b_null == null)
+        assert_true(b_null is null_blob)
+        assert_true(b_null == null_blob)
+
+        add(b_decl, 6)
+        assert_equal(0z06, b_decl)
+        add(b_empty, 7)
+        assert_equal(0z07, b_empty)
+        var caught = false
+        try
+          add(b_null, 9)
+        catch /E1131:/
+          caught = true
+        endtry
+        assert_true(caught)
+      enddef
+      TestBlob()
+      
+      var d_decl: dict<number>
+      var d_empty = {}
+      var d_null = null_dict
+
+      def TestDict()
+        assert_false(d_decl == null)
+        assert_false(d_decl is null_dict)
+        assert_false(d_empty == null)
+        assert_false(d_empty is null_dict)
+        assert_true(d_null == null)
+        assert_true(d_null is null_dict)
+        assert_true(d_null == null_dict)
+
+        d_decl['a'] = 6
+        assert_equal({a: 6}, d_decl)
+        d_empty['b'] = 7
+        assert_equal({b: 7}, d_empty)
+        var caught = false
+        try
+          d_null['c'] = 9
+        catch /E1103:/
+          caught = true
+        endtry
+        assert_true(caught)
+      enddef
+      TestDict()
+  END
+  v9.CheckScriptSuccess(lines)
+
   lines =<< trim END
       var d: dict<func> = {f: null_function}
       assert_equal(null_function, d.f)
index 0c1a16670a9a3d05fbe57604201d947b35c4d139..9d3afeb1657a2b68ca15fc0c8a0b263664cccd99 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4642,
 /**/
     4641,
 /**/
index 13ce0327385a367e9de76986517ac40d5d9d354f..d9bd51b1aa8a00da681d10aa8c9cc73a208aefab 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2229,6 +2229,7 @@ typedef enum {
 #define ASSIGN_UNPACK  0x10  // using [a, b] = list
 #define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type
 #define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable
+#define ASSIGN_INIT    0x80 // not assigning a value, just a declaration
 
 #include "ex_cmds.h"       // Ex command defines
 #include "spell.h"         // spell checking stuff
index 0404eb4123739c4df8d6b9e4ad380e061aa935f0..a95a488068b87563215efc2a7a1493bdf1141560 100644 (file)
@@ -1336,20 +1336,22 @@ do_2string(typval_T *tv, int is_2string_any, int tolerant)
  * When the value of "sv" is a null list of dict, allocate it.
  */
     static void
-allocate_if_null(typval_T *tv)
+allocate_if_null(svar_T *sv)
 {
+    typval_T *tv = sv->sv_tv;
+
     switch (tv->v_type)
     {
        case VAR_LIST:
-           if (tv->vval.v_list == NULL)
+           if (tv->vval.v_list == NULL && sv->sv_type != &t_list_empty)
                (void)rettv_list_alloc(tv);
            break;
        case VAR_DICT:
-           if (tv->vval.v_dict == NULL)
+           if (tv->vval.v_dict == NULL && sv->sv_type != &t_dict_empty)
                (void)rettv_dict_alloc(tv);
            break;
        case VAR_BLOB:
-           if (tv->vval.v_blob == NULL)
+           if (tv->vval.v_blob == NULL && sv->sv_type != &t_blob_null)
                (void)rettv_blob_alloc(tv);
            break;
        default:
@@ -2891,7 +2893,7 @@ exec_instructions(ectx_T *ectx)
                    sv = get_script_svar(sref, ectx->ec_dfunc_idx);
                    if (sv == NULL)
                        goto theend;
-                   allocate_if_null(sv->sv_tv);
+                   allocate_if_null(sv);
                    if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                        goto theend;
                    copy_tv(sv->sv_tv, STACK_TV_BOT(0));
index 3e8fe2f65354b2ce7de56f9d622705ab8e180a85..b793110b9229d5e0cdfc1c29c39e1849d5a2e5fe 100644 (file)
@@ -822,7 +822,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
        init_tv.v_type = VAR_NUMBER;
     else
        init_tv.v_type = type->tt_type;
-    set_var_const(name, 0, type, &init_tv, FALSE, 0, 0);
+    set_var_const(name, 0, type, &init_tv, FALSE, ASSIGN_INIT, 0);
 
     vim_free(name);
     return p;
@@ -925,6 +925,13 @@ update_vim9_script_var(
        if (*type == NULL)
            *type = typval2type(tv, get_copyID(), &si->sn_type_list,
                                               do_member ? TVTT_DO_MEMBER : 0);
+       else if ((flags & ASSIGN_INIT) == 0
+               && (*type)->tt_type == VAR_BLOB && tv->v_type == VAR_BLOB
+                                                   && tv->vval.v_blob == NULL)
+       {
+           // "var b: blob = null_blob" has a different type.
+           *type = &t_blob_null;
+       }
        if (sv->sv_type_allocated)
            free_type(sv->sv_type);
        if (*type != NULL && ((*type)->tt_type == VAR_FUNC
index b6687b0896eddb294f5c02aa205ad0e5dc7691bb..ff259f4941daf7ead3425783a9af5fb44bf49c13 100644 (file)
@@ -337,7 +337,11 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
     if (tv->v_type == VAR_STRING)
        return &t_string;
     if (tv->v_type == VAR_BLOB)
+    {
+       if (tv->vval.v_blob == NULL)
+           return &t_blob_null;
        return &t_blob;
+    }
 
     if (tv->v_type == VAR_LIST)
     {