diff src/vim9instr.c @ 30333:fc0830246f49 v9.0.0502

patch 9.0.0502: a closure in a nested loop in a :def function does not work Commit: https://github.com/vim/vim/commit/cc34181f9994d64f8c8fa2f5845eaf0cc963067f Author: Bram Moolenaar <Bram@vim.org> Date: Mon Sep 19 15:54:34 2022 +0100 patch 9.0.0502: a closure in a nested loop in a :def function does not work Problem: A closure in a nested loop in a :def function does not work. Solution: Use an array of loopvars, one per loop level.
author Bram Moolenaar <Bram@vim.org>
date Mon, 19 Sep 2022 17:00:07 +0200
parents 029c59bf78f1
children 72e6073a2822
line wrap: on
line diff
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -997,6 +997,7 @@ generate_LOADOUTER(
 	cctx_T	    *cctx,
 	int	    idx,
 	int	    nesting,
+	int	    loop_depth,
 	int	    loop_idx,
 	type_T	    *type)
 {
@@ -1008,9 +1009,9 @@ generate_LOADOUTER(
     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?
+	// of the loop.
 	isn->isn_arg.outer.outer_idx = idx - loop_idx;
-	isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
+	isn->isn_arg.outer.outer_depth = -loop_depth - 1;
     }
     else
     {
@@ -1207,8 +1208,8 @@ generate_FUNCREF(
     isn_T	    *isn;
     type_T	    *type;
     funcref_extra_T *extra;
-    short	    loop_var_idx;
-    short	    loop_var_count;
+    loopvarinfo_T   loopinfo;
+    int		    has_vars;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
@@ -1216,20 +1217,22 @@ generate_FUNCREF(
     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)
+    has_vars = get_loop_var_info(cctx, &loopinfo);
+    if (ufunc->uf_def_status == UF_NOT_COMPILED || has_vars)
     {
 	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;
+	extra->fre_loopvar_info = loopinfo;
     }
     if (ufunc->uf_def_status == UF_NOT_COMPILED)
 	extra->fre_func_name = vim_strsave(ufunc->uf_name);
     else
 	isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
+
+    // Reserve an extra variable to keep track of the number of closures
+    // created.
     cctx->ctx_has_closure = 1;
 
     // If the referenced function is a closure, it may use items further up in
@@ -1252,9 +1255,7 @@ generate_FUNCREF(
 generate_NEWFUNC(
 	cctx_T	*cctx,
 	char_u	*lambda_name,
-	char_u	*func_name,
-	short	loop_var_idx,
-	short	loop_var_count)
+	char_u	*func_name)
 {
     isn_T	*isn;
     int		ret = OK;
@@ -1271,11 +1272,14 @@ generate_NEWFUNC(
 		ret = FAIL;
 	    else
 	    {
+		// Reserve an extra variable to keep track of the number of
+		// closures created.
+		cctx->ctx_has_closure = 1;
+
 		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;
+		(void)get_loop_var_info(cctx, &arg->nfa_loopvar_info);
 		return OK;
 	    }
 	}
@@ -1371,27 +1375,25 @@ generate_FOR(cctx_T *cctx, int loop_idx)
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
 	return FAIL;
-    isn->isn_arg.forloop.for_idx = loop_idx;
+    isn->isn_arg.forloop.for_loop_idx = loop_idx;
 
     // type doesn't matter, will be stored next
     return push_type_stack(cctx, &t_any);
 }
 
     int
-generate_ENDLOOP(
-	cctx_T	*cctx,
-	int	funcref_idx,
-	int	prev_local_count)
+generate_ENDLOOP(cctx_T *cctx, loop_info_T *loop_info)
 {
     isn_T	*isn;
 
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_ENDLOOP)) == NULL)
 	return FAIL;
-    isn->isn_arg.endloop.end_funcref_idx = funcref_idx;
-    isn->isn_arg.endloop.end_var_idx = prev_local_count;
+    isn->isn_arg.endloop.end_depth = loop_info->li_depth;
+    isn->isn_arg.endloop.end_funcref_idx = loop_info->li_funcref_idx;
+    isn->isn_arg.endloop.end_var_idx = loop_info->li_local_count;
     isn->isn_arg.endloop.end_var_count =
-				    cctx->ctx_locals.ga_len - prev_local_count;
+			   cctx->ctx_locals.ga_len - loop_info->li_local_count;
     return OK;
 }