patch 9.0.0481: 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:   Use a separate list of variables for LOADOUTER and STOREOUTER.
            Not copied at end of loop yet.
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 46f0b36..11e39f1 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -916,15 +916,25 @@
  * Generate an ISN_STOREOUTER instruction.
  */
     static int
-generate_STOREOUTER(cctx_T *cctx, int idx, int level)
+generate_STOREOUTER(cctx_T *cctx, int idx, int level, int loop_idx)
 {
     isn_T	*isn;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
 	return FAIL;
-    isn->isn_arg.outer.outer_idx = idx;
-    isn->isn_arg.outer.outer_depth = level;
+    if (level == 1 && loop_idx >= 0 && idx >= loop_idx)
+    {
+	// Store a variable defined in a loop.  A copy will be made at the end
+	// of the loop.  TODO: how about deeper nesting?
+	isn->isn_arg.outer.outer_idx = idx - loop_idx;
+	isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+    }
+    else
+    {
+	isn->isn_arg.outer.outer_idx = idx;
+	isn->isn_arg.outer.outer_depth = level;
+    }
 
     return OK;
 }
@@ -999,6 +1009,7 @@
 	cctx_T	    *cctx,
 	int	    idx,
 	int	    nesting,
+	int	    loop_idx,
 	type_T	    *type)
 {
     isn_T	*isn;
@@ -1006,8 +1017,18 @@
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
 	return FAIL;
-    isn->isn_arg.outer.outer_idx = idx;
-    isn->isn_arg.outer.outer_depth = nesting;
+    if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
+    {
+	// Load a variable defined in a loop.  A copy will be made at the end
+	// of the loop.  TODO: how about deeper nesting?
+	isn->isn_arg.outer.outer_idx = idx - loop_idx;
+	isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+    }
+    else
+    {
+	isn->isn_arg.outer.outer_idx = idx;
+	isn->isn_arg.outer.outer_depth = nesting;
+    }
 
     return OK;
 }
@@ -1186,20 +1207,39 @@
 /*
  * Generate an ISN_FUNCREF instruction.
  * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
+ * If variables were declared inside a loop "loop_var_idx" is the index of the
+ * first one and "loop_var_count" the number of variables declared.
  */
     int
-generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
+generate_FUNCREF(
+	cctx_T	    *cctx,
+	ufunc_T	    *ufunc,
+	isn_T	    **isnp)
 {
-    isn_T	*isn;
-    type_T	*type;
+    isn_T	    *isn;
+    type_T	    *type;
+    funcref_extra_T *extra;
+    short	    loop_var_idx;
+    short	    loop_var_count;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
 	return FAIL;
     if (isnp != NULL)
 	*isnp = isn;
+
+    loop_var_count = get_loop_var_info(cctx, &loop_var_idx);
+    if (ufunc->uf_def_status == UF_NOT_COMPILED || loop_var_count > 0)
+    {
+	extra = ALLOC_CLEAR_ONE(funcref_extra_T);
+	if (extra == NULL)
+	    return FAIL;
+	isn->isn_arg.funcref.fr_extra = extra;
+	extra->fre_loop_var_idx = loop_var_idx;
+	extra->fre_loop_var_count = loop_var_count;
+    }
     if (ufunc->uf_def_status == UF_NOT_COMPILED)
-	isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
+	extra->fre_func_name = vim_strsave(ufunc->uf_name);
     else
 	isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
     cctx->ctx_has_closure = 1;
@@ -1221,7 +1261,12 @@
  * consumed.
  */
     int
-generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
+generate_NEWFUNC(
+	cctx_T	*cctx,
+	char_u	*lambda_name,
+	char_u	*func_name,
+	short	loop_var_idx,
+	short	loop_var_count)
 {
     isn_T	*isn;
     int		ret = OK;
@@ -1232,9 +1277,19 @@
 	    ret = FAIL;
 	else
 	{
-	    isn->isn_arg.newfunc.nf_lambda = lambda_name;
-	    isn->isn_arg.newfunc.nf_global = func_name;
-	    return OK;
+	    newfuncarg_T *arg = ALLOC_CLEAR_ONE(newfuncarg_T);
+
+	    if (arg == NULL)
+		ret = FAIL;
+	    else
+	    {
+		isn->isn_arg.newfunc.nf_arg = arg;
+		arg->nfa_lambda = lambda_name;
+		arg->nfa_global = func_name;
+		arg->nfa_loop_var_idx = loop_var_idx;
+		arg->nfa_loop_var_count = loop_var_count;
+		return OK;
+	    }
 	}
     }
     vim_free(lambda_name);
@@ -2123,7 +2178,7 @@
 	}
 	else if (lhs->lhs_lvar->lv_from_outer > 0)
 	    generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx,
-						 lhs->lhs_lvar->lv_from_outer);
+		     lhs->lhs_lvar->lv_from_outer, lhs->lhs_lvar->lv_loop_idx);
 	else
 	    generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
     }
@@ -2226,22 +2281,28 @@
 
 	case ISN_FUNCREF:
 	    {
-		if (isn->isn_arg.funcref.fr_func_name == NULL)
+		funcref_T	*funcref = &isn->isn_arg.funcref;
+		funcref_extra_T *extra = funcref->fr_extra;
+
+		if (extra == NULL || extra->fre_func_name == NULL)
 		{
 		    dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
-			+ isn->isn_arg.funcref.fr_dfunc_idx;
+						       + funcref->fr_dfunc_idx;
 		    ufunc_T *ufunc = dfunc->df_ufunc;
 
 		    if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
 			func_ptr_unref(ufunc);
 		}
-		else
+		if (extra != NULL)
 		{
-		    char_u *name = isn->isn_arg.funcref.fr_func_name;
+		    char_u *name = extra->fre_func_name;
 
 		    if (name != NULL)
+		    {
 			func_unref(name);
-		    vim_free(isn->isn_arg.funcref.fr_func_name);
+			vim_free(name);
+		    }
+		    vim_free(extra);
 		}
 	    }
 	    break;
@@ -2259,17 +2320,23 @@
 
 	case ISN_NEWFUNC:
 	    {
-		char_u  *lambda = isn->isn_arg.newfunc.nf_lambda;
-		ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
+		newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
 
-		if (ufunc != NULL)
+		if (arg != NULL)
 		{
-		    unlink_def_function(ufunc);
-		    func_ptr_unref(ufunc);
-		}
+		    ufunc_T *ufunc = find_func_even_dead(
+					      arg->nfa_lambda, FFED_IS_GLOBAL);
 
-		vim_free(lambda);
-		vim_free(isn->isn_arg.newfunc.nf_global);
+		    if (ufunc != NULL)
+		    {
+			unlink_def_function(ufunc);
+			func_ptr_unref(ufunc);
+		    }
+
+		    vim_free(arg->nfa_lambda);
+		    vim_free(arg->nfa_global);
+		    vim_free(arg);
+		}
 	    }
 	    break;