changeset 28345:fabe722b24e9 v8.2.4698

patch 8.2.4698: Vim9: script variable has no flag that it was set Commit: https://github.com/vim/vim/commit/aa7d0c233532fb9d8c2876ea8e978a82b12c377f Author: Bram Moolenaar <Bram@vim.org> Date: Tue Apr 5 21:40:38 2022 +0100 patch 8.2.4698: Vim9: script variable has no flag that it was set Problem: Vim9: script variable has no flag that it was set. Solution: Add a flag that it was set, to avoid giving it a value when used. (closes #10088)
author Bram Moolenaar <Bram@vim.org>
date Tue, 05 Apr 2022 22:45:03 +0200
parents 10a99c04ba1d
children e080546fb0f8
files src/evalvars.c src/structs.h src/testdir/test_vim9_assign.vim src/testdir/test_vim9_builtin.vim src/version.c src/vim9execute.c src/vim9script.c
diffstat 7 files changed, 71 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2828,13 +2828,18 @@ eval_variable(
 	}
 	else if (rettv != NULL)
 	{
+	    svar_T  *sv = NULL;
+	    int	    was_assigned = FALSE;
+
 	    if (ht != NULL && ht == get_script_local_ht()
 		    && tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv)
 	    {
-		svar_T *sv = find_typval_in_script(tv, 0, TRUE);
-
+		sv = find_typval_in_script(tv, 0, TRUE);
 		if (sv != NULL)
+		{
 		    type = sv->sv_type;
+		    was_assigned = sv->sv_flags & SVFLAG_ASSIGNED;
+		}
 	    }
 
 	    // If a list or dict variable wasn't initialized and has meaningful
@@ -2843,7 +2848,7 @@ eval_variable(
 	    if (ht != &globvarht)
 	    {
 		if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
-			  && ((type != NULL && type != &t_dict_empty)
+					    && ((type != NULL && !was_assigned)
 							  || !in_vim9script()))
 		{
 		    tv->vval.v_dict = dict_alloc();
@@ -2851,10 +2856,12 @@ eval_variable(
 		    {
 			++tv->vval.v_dict->dv_refcount;
 			tv->vval.v_dict->dv_type = alloc_type(type);
+			if (sv != NULL)
+			    sv->sv_flags |= SVFLAG_ASSIGNED;
 		    }
 		}
 		else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
-				    && ((type != NULL && type != &t_list_empty)
+					    && ((type != NULL && !was_assigned)
 							  || !in_vim9script()))
 		{
 		    tv->vval.v_list = list_alloc();
@@ -2862,15 +2869,21 @@ eval_variable(
 		    {
 			++tv->vval.v_list->lv_refcount;
 			tv->vval.v_list->lv_type = alloc_type(type);
+			if (sv != NULL)
+			    sv->sv_flags |= SVFLAG_ASSIGNED;
 		    }
 		}
 		else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
-				    && ((type != NULL && type != &t_blob_null)
+					    && ((type != NULL && !was_assigned)
 							  || !in_vim9script()))
 		{
 		    tv->vval.v_blob = blob_alloc();
 		    if (tv->vval.v_blob != NULL)
+		    {
 			++tv->vval.v_blob->bv_refcount;
+			if (sv != NULL)
+			    sv->sv_flags |= SVFLAG_ASSIGNED;
+		    }
 		}
 	    }
 	    copy_tv(tv, rettv);
@@ -3587,6 +3600,7 @@ set_var_const(
 			goto failed;
 		    if (type == NULL)
 			type = sv->sv_type;
+		    sv->sv_flags |= SVFLAG_ASSIGNED;
 		}
 	    }
 
--- a/src/structs.h
+++ b/src/structs.h
@@ -1807,6 +1807,10 @@ struct sallvar_S {
 #define HIKEY2SAV(p)  ((sallvar_T *)(p - offsetof(sallvar_T, sav_key)))
 #define HI2SAV(hi)     HIKEY2SAV((hi)->hi_key)
 
+#define SVFLAG_TYPE_ALLOCATED	1  // call free_type() for "sv_type"
+#define SVFLAG_EXPORTED		2  // "export let var = val"
+#define SVFLAG_ASSIGNED		4  // assigned a value
+
 /*
  * Entry for "sn_var_vals".  Used for script-local variables.
  */
@@ -1814,9 +1818,8 @@ struct svar_S {
     char_u	*sv_name;	// points into "sn_all_vars" di_key
     typval_T	*sv_tv;		// points into "sn_vars" or "sn_all_vars" di_tv
     type_T	*sv_type;
-    int		sv_type_allocated;  // call free_type() for sv_type
+    int		sv_flags;	// SVFLAG_ values above
     int		sv_const;	// 0, ASSIGN_CONST or ASSIGN_FINAL
-    int		sv_export;	// "export let var = val"
 };
 
 typedef struct {
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -740,6 +740,7 @@ def Test_init_in_for_loop()
 enddef
 
 def Test_extend_list()
+  # using uninitilaized list assigns empty list
   var lines =<< trim END
       var l1: list<number>
       var l2 = l1
@@ -757,7 +758,7 @@ def Test_extend_list()
   END
   v9.CheckDefAndScriptSuccess(lines)
 
-  # appending to NULL list from a function
+  # appending to uninitialzed list from a function works
   lines =<< trim END
       vim9script
       var list: list<string>
@@ -779,13 +780,30 @@ def Test_extend_list()
   END
   v9.CheckScriptSuccess(lines)
 
+  # initialized to null, with type, does not default to empty list
   lines =<< trim END
       vim9script
       var l: list<string> = test_null_list()
       extend(l, ['x'])
-      assert_equal(['x'], l)
+  END
+  v9.CheckScriptFailure(lines, 'E1134:', 3)
+
+  # initialized to null, without type, does not default to empty list
+  lines =<< trim END
+      vim9script
+      var l = null_list
+      extend(l, ['x'])
   END
-  v9.CheckScriptSuccess(lines)
+  v9.CheckScriptFailure(lines, 'E1134:', 3)
+
+  # assigned null, does not default to empty list
+  lines =<< trim END
+      vim9script
+      var l: list<string>
+      l = null_list
+      extend(l, ['x'])
+  END
+  v9.CheckScriptFailure(lines, 'E1134:', 4)
 
   lines =<< trim END
       vim9script
@@ -838,9 +856,8 @@ def Test_extend_dict()
       vim9script
       var d: dict<string> = test_null_dict()
       extend(d, {a: 'x'})
-      assert_equal({a: 'x'}, d)
   END
-  v9.CheckScriptSuccess(lines)
+  v9.CheckScriptFailure(lines, 'E1133:', 3)
 
   lines =<< trim END
       vim9script
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -153,13 +153,21 @@ def Test_add_list()
   END
   v9.CheckDefExecFailure(lines, 'E1130:', 2)
 
-  # Getting variable with NULL list allocates a new list at script level
+  # Getting an uninitialized variable allocates a new list at script level
+  lines =<< trim END
+      vim9script
+      var l: list<number>
+      add(l, 123)
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # Adding to a variable set to a NULL list fails
   lines =<< trim END
       vim9script
       var l: list<number> = test_null_list()
       add(l, 123)
   END
-  v9.CheckScriptSuccess(lines)
+  v9.CheckScriptFailure(lines, 'E1130:', 3)
 
   lines =<< trim END
       vim9script
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4698,
+/**/
     4697,
 /**/
     4696,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1514,7 +1514,8 @@ get_script_svar(scriptref_T *sref, int d
 	return NULL;
     }
 
-    if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid)
+    if ((sv->sv_flags & SVFLAG_EXPORTED) == 0
+				      && sref->sref_sid != current_sctx.sc_sid)
     {
 	if (dfunc != NULL)
 	    semsg(_(e_item_not_exported_in_script_str), sv->sv_name);
@@ -2952,7 +2953,7 @@ exec_instructions(ectx_T *ectx)
 			    {
 				sv = ((svar_T *)SCRIPT_ITEM(sid)
 						  ->sn_var_vals.ga_data) + idx;
-				if (!sv->sv_export)
+				if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
 				{
 				    SOURCING_LNUM = iptr->isn_lnum;
 				    semsg(_(e_item_not_exported_in_script_str),
@@ -3117,7 +3118,7 @@ exec_instructions(ectx_T *ectx)
 				svar_T	*sv = ((svar_T *)SCRIPT_ITEM(sid)
 						  ->sn_var_vals.ga_data) + idx;
 
-				if (!sv->sv_export)
+				if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
 				{
 				    semsg(_(e_item_not_exported_in_script_str),
 									 name);
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -334,7 +334,7 @@ free_all_script_vars(scriptitem_T *si)
     {
 	svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
 
-	if (sv->sv_type_allocated)
+	if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED)
 	    free_type(sv->sv_type);
     }
     ga_clear(&si->sn_var_vals);
@@ -721,7 +721,7 @@ find_exported(
     {
 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
 	*ufunc = NULL;
-	if (!sv->sv_export)
+	if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
 	{
 	    if (verbose)
 		semsg(_(e_item_not_exported_in_script_str), name);
@@ -871,7 +871,7 @@ vim9_declare_scriptvar(exarg_T *eap, cha
  * with a hashtable) and sn_var_vals (lookup by index).
  * When "create" is TRUE this is a new variable, otherwise find and update an
  * existing variable.
- * "flags" can have ASSIGN_FINAL or ASSIGN_CONST.
+ * "flags" can have ASSIGN_FINAL, ASSIGN_CONST or ASSIGN_INIT.
  * When "*type" is NULL use "tv" for the type and update "*type".  If
  * "do_member" is TRUE also use the member type, otherwise use "any".
  */
@@ -938,7 +938,9 @@ update_vim9_script_var(
 	    sv->sv_tv = &di->di_tv;
 	    sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL
 				   : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0;
-	    sv->sv_export = is_export;
+	    sv->sv_flags = is_export ? SVFLAG_EXPORTED : 0;
+	    if ((flags & ASSIGN_INIT) == 0)
+		sv->sv_flags |= SVFLAG_ASSIGNED;
 	    newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
 	    ++si->sn_var_vals.ga_len;
 	    STRCPY(&newsav->sav_key, name);
@@ -970,7 +972,7 @@ update_vim9_script_var(
 	    // "var b: blob = null_blob" has a different type.
 	    *type = &t_blob_null;
 	}
-	if (sv->sv_type_allocated)
+	if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED)
 	    free_type(sv->sv_type);
 	if (*type != NULL && ((*type)->tt_type == VAR_FUNC
 					   || (*type)->tt_type == VAR_PARTIAL))
@@ -979,12 +981,12 @@ update_vim9_script_var(
 	    // function is freed, but the script variable may keep the type.
 	    // Make a copy to avoid using freed memory.
 	    sv->sv_type = alloc_type(*type);
-	    sv->sv_type_allocated = TRUE;
+	    sv->sv_flags |= SVFLAG_TYPE_ALLOCATED;
 	}
 	else
 	{
 	    sv->sv_type = *type;
-	    sv->sv_type_allocated = FALSE;
+	    sv->sv_flags &= ~SVFLAG_TYPE_ALLOCATED;
 	}
     }