patch 9.0.0470: in :def function all closures in loop get the same variables

Problem:    In a :def function all closures in a loop get the same variables.
Solution:   When in a loop and a closure refers to a variable declared in the
            loop, prepare for making a copy of variables for each closure.
diff --git a/src/vim9execute.c b/src/vim9execute.c
index cbf9af7..a1c2f8b 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -504,7 +504,8 @@
     // - if needed: a counter for number of closures created in
     //   ectx->ec_funcrefs.
     varcount = dfunc->df_varcount + dfunc->df_has_closure;
-    if (GA_GROW_FAILS(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount))
+    if (GA_GROW_FAILS(&ectx->ec_stack,
+				     arg_to_add + STACK_FRAME_SIZE + varcount))
 	return FAIL;
 
     // If depth of calling is getting too high, don't execute the function.
@@ -553,6 +554,8 @@
     {
 	typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
 
+	// Initialize the variable that counts how many closures were created.
+	// This is used in handle_closure_in_use().
 	tv->v_type = VAR_NUMBER;
 	tv->vval.v_number = 0;
     }
@@ -1821,8 +1824,8 @@
 	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
 							  + ectx->ec_dfunc_idx;
 
-	// The closure may need to find arguments and local variables in the
-	// current stack.
+	// The closure may need to find arguments and local variables of the
+	// current function in the stack.
 	pt->pt_outer.out_stack = &ectx->ec_stack;
 	pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
 	if (ectx->ec_outer_ref != NULL)
@@ -1836,8 +1839,9 @@
 	    }
 	}
 
-	// If this function returns and the closure is still being used, we
-	// need to make a copy of the context (arguments and local variables).
+	// If the function currently executing returns and the closure is still
+	// being referenced, we need to make a copy of the context (arguments
+	// and local variables) so that the closure can use it later.
 	// Store a reference to the partial so we can handle that.
 	if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
 	{
@@ -2477,6 +2481,7 @@
 execute_for(isn_T *iptr, ectx_T *ectx)
 {
     typval_T	*tv;
+    int		jump = FALSE;
     typval_T	*ltv = STACK_TV_BOT(-1);
     typval_T	*idxtv =
 		   STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
@@ -2492,9 +2497,7 @@
 	if (list == NULL
 		       || idxtv->vval.v_number >= list->lv_len)
 	{
-	    // past the end of the list, jump to "endfor"
-	    ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
-	    may_restore_cmdmod(&ectx->ec_funclocal);
+	    jump = TRUE;
 	}
 	else if (list->lv_first == &range_list_item)
 	{
@@ -2524,9 +2527,7 @@
 	++idxtv->vval.v_number;
 	if (str == NULL || str[idxtv->vval.v_number] == NUL)
 	{
-	    // past the end of the string, jump to "endfor"
-	    ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
-	    may_restore_cmdmod(&ectx->ec_funclocal);
+	    jump = TRUE;
 	}
 	else
 	{
@@ -2557,12 +2558,9 @@
 
 	// The index is for the previous byte.
 	++idxtv->vval.v_number;
-	if (blob == NULL
-		     || idxtv->vval.v_number >= blob_len(blob))
+	if (blob == NULL || idxtv->vval.v_number >= blob_len(blob))
 	{
-	    // past the end of the blob, jump to "endfor"
-	    ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
-	    may_restore_cmdmod(&ectx->ec_funclocal);
+	    jump = TRUE;
 	}
 	else
 	{
@@ -2580,6 +2578,33 @@
 				    vartype_name(ltv->v_type));
 	return FAIL;
     }
+
+    if (jump)
+    {
+	// past the end of the list/string/blob, jump to "endfor"
+	ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
+	may_restore_cmdmod(&ectx->ec_funclocal);
+    }
+    else
+    {
+	// Store the current number of funcrefs, this may be used in
+	// ISN_LOOPEND.  The variable index is always one more than the loop
+	// variable index.
+	tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx + 1);
+	tv->vval.v_number = ectx->ec_funcrefs.ga_len;
+    }
+
+    return OK;
+}
+
+/*
+ * End of a for or while loop: Handle any variables used by a closure.
+ */
+    static int
+execute_endloop(isn_T *iptr UNUSED, ectx_T *ectx UNUSED)
+{
+    // TODO
+
     return OK;
 }
 
@@ -3989,6 +4014,31 @@
 		}
 		break;
 
+	    // "while": jump to end if a condition is false
+	    case ISN_WHILE:
+		{
+		    int		error = FALSE;
+		    int		jump = TRUE;
+
+		    tv = STACK_TV_BOT(-1);
+		    SOURCING_LNUM = iptr->isn_lnum;
+		    jump = !tv_get_bool_chk(tv, &error);
+		    if (error)
+			goto on_error;
+		    // drop the value from the stack
+		    clear_tv(tv);
+		    --ectx->ec_stack.ga_len;
+		    if (jump)
+			ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
+
+		    // Store the current funccal count, may be used by
+		    // ISN_LOOPEND later
+		    tv = STACK_TV_VAR(
+				    iptr->isn_arg.whileloop.while_funcref_idx);
+		    tv->vval.v_number = ectx->ec_funcrefs.ga_len;
+		}
+		break;
+
 	    // Jump if an argument with a default value was already set and not
 	    // v:none.
 	    case ISN_JUMP_IF_ARG_SET:
@@ -4005,6 +4055,12 @@
 		    goto theend;
 		break;
 
+	    // end of a for or while loop
+	    case ISN_ENDLOOP:
+		if (execute_endloop(iptr, ectx) == FAIL)
+		    goto theend;
+		break;
+
 	    // start of ":try" block
 	    case ISN_TRY:
 		{
@@ -6185,6 +6241,9 @@
 			case JUMP_IF_FALSE:
 			    when = "JUMP_IF_FALSE";
 			    break;
+			case JUMP_WHILE_FALSE:
+			    when = "JUMP_WHILE_FALSE";  // unused
+			    break;
 			case JUMP_IF_COND_FALSE:
 			    when = "JUMP_IF_COND_FALSE";
 			    break;
@@ -6212,6 +6271,27 @@
 		}
 		break;
 
+	    case ISN_ENDLOOP:
+		{
+		    endloop_T *endloop = &iptr->isn_arg.endloop;
+
+		    smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current,
+			    endloop->end_funcref_idx,
+			    endloop->end_var_idx,
+			    endloop->end_var_idx + endloop->end_var_count - 1);
+		}
+		break;
+
+	    case ISN_WHILE:
+		{
+		    whileloop_T *whileloop = &iptr->isn_arg.whileloop;
+
+		    smsg("%s%4d WHILE $%d -> %d", pfx, current,
+					       whileloop->while_funcref_idx,
+					       whileloop->while_end);
+		}
+		break;
+
 	    case ISN_TRY:
 		{
 		    try_T *try = &iptr->isn_arg.tryref;