changeset 27148:6ed31017c303 v8.2.4103

patch 8.2.4103: Vim9: variable declared in for loop not initialzed Commit: https://github.com/vim/vim/commit/38ecd9722664049d636f4fba759b3ebbfd34e97d Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 15 21:44:44 2022 +0000 patch 8.2.4103: Vim9: variable declared in for loop not initialzed Problem: Vim9: variable declared in for loop not initialzed. Solution: Always initialze the variable. (closes https://github.com/vim/vim/issues/9535)
author Bram Moolenaar <Bram@vim.org>
date Sat, 15 Jan 2022 22:45:03 +0100
parents bcaefb769725
children 5ec89814c0bf
files src/proto/vim9instr.pro src/testdir/test_vim9_assign.vim src/version.c src/vim9compile.c src/vim9instr.c
diffstat 5 files changed, 67 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -63,6 +63,7 @@ int generate_UNPACK(cctx_T *cctx, int va
 int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
 int generate_undo_cmdmods(cctx_T *cctx);
 int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name);
+int inside_loop_scope(cctx_T *cctx);
 int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl);
 void may_generate_prof_end(cctx_T *cctx, int prof_lnum);
 void delete_instr(isn_T *isn);
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -587,6 +587,41 @@ def Test_assign_index()
   CheckDefFailure(lines, 'E1012: Type mismatch; expected list<number> but got dict<unknown>', 2)
 enddef
 
+def Test_init_in_for_loop()
+  var lines =<< trim END
+      var l: list<number> = []
+      for i in [3, 4]
+        var n: number
+        add(l, n)
+        n = 123
+      endfor
+      assert_equal([0, 0], l)
+  END
+  CheckDefAndScriptSuccess(lines)
+
+  lines =<< trim END
+      var l: list<number> = []
+      for i in [3, 4]
+        var n: number = 0
+        add(l, n)
+        n = 123
+      endfor
+      assert_equal([0, 0], l)
+  END
+  CheckDefAndScriptSuccess(lines)
+
+  lines =<< trim END
+      var l: list<number> = []
+      for i in [3, 4]
+        var n: number = 3
+        add(l, n)
+        n = 123
+      endfor
+      assert_equal([3, 3], l)
+  END
+  CheckDefAndScriptSuccess(lines)
+enddef
+
 def Test_extend_list()
   var lines =<< trim END
       var l1: list<number>
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4103,
+/**/
     4102,
 /**/
     4101,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2256,12 +2256,17 @@ compile_assignment(char_u *arg, exarg_T 
 		    case VAR_VOID:
 		    case VAR_INSTR:
 		    case VAR_SPECIAL:  // cannot happen
-			// This is skipped for local variables, they are
-			// always initialized to zero.
-			if (lhs.lhs_dest == dest_local)
+			// This is skipped for local variables, they are always
+			// initialized to zero.  But in a "for" or "while" loop
+			// the value may have been changed.
+			if (lhs.lhs_dest == dest_local
+						   && !inside_loop_scope(cctx))
 			    skip_store = TRUE;
 			else
+			{
+			    instr_count = instr->ga_len;
 			    generate_PUSHNR(cctx, 0);
+			}
 			break;
 		}
 	    }
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1845,6 +1845,25 @@ generate_store_var(
     return FAIL;
 }
 
+/*
+ * Return TRUE when inside a "for" or "while" loop.
+ */
+    int
+inside_loop_scope(cctx_T *cctx)
+{
+    scope_T	*scope = cctx->ctx_scope;
+
+    for (;;)
+    {
+	if (scope == NULL)
+	    break;
+	if (scope->se_type == FOR_SCOPE || scope->se_type == WHILE_SCOPE)
+	    return TRUE;
+	scope = scope->se_outer;
+    }
+    return FALSE;
+}
+
     int
 generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
 {
@@ -1869,8 +1888,9 @@ generate_store_lhs(cctx_T *cctx, lhs_T *
 	    varnumber_T val = isn->isn_arg.number;
 	    garray_T    *stack = &cctx->ctx_type_stack;
 
-	    if (val == 0 && is_decl)
+	    if (val == 0 && is_decl && !inside_loop_scope(cctx))
 	    {
+		// zero is the default value, no need to do anything
 		--instr->ga_len;
 	    }
 	    else