patch 8.2.2321: Vim9: cannot nest closures
Problem: Vim9: cannot nest closures.
Solution: Add the nesting level to ISN_LOADOUTER and ISN_STOREOUTER.
(closes #7150, closes #7635)
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 7720020..a6bb565 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -108,7 +108,7 @@
char_u *lv_name;
type_T *lv_type;
int lv_idx; // index of the variable on the stack
- int lv_from_outer; // when TRUE using ctx_outer scope
+ int lv_from_outer; // nesting level, using ctx_outer scope
int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument
} lvar_T;
@@ -149,7 +149,7 @@
/*
* Lookup variable "name" in the local scope and return it in "lvar".
- * "lvar->lv_from_outer" is set accordingly.
+ * "lvar->lv_from_outer" is incremented accordingly.
* If "lvar" is NULL only check if the variable can be found.
* Return FAIL if not found.
*/
@@ -172,7 +172,7 @@
if (lvar != NULL)
{
*lvar = *lvp;
- lvar->lv_from_outer = FALSE;
+ lvar->lv_from_outer = 0;
}
return OK;
}
@@ -186,7 +186,7 @@
if (lvar != NULL)
{
cctx->ctx_outer_used = TRUE;
- lvar->lv_from_outer = TRUE;
+ ++lvar->lv_from_outer;
}
return OK;
}
@@ -258,7 +258,7 @@
if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
== OK)
{
- *gen_load_outer = TRUE;
+ ++*gen_load_outer;
return OK;
}
}
@@ -1176,6 +1176,23 @@
}
/*
+ * Generate an ISN_STOREOUTER instruction.
+ */
+ static int
+generate_STOREOUTER(cctx_T *cctx, int idx, int level)
+{
+ 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;
+
+ return OK;
+}
+
+/*
* Generate an ISN_STORENR instruction (short for ISN_PUSHNR + ISN_STORE)
*/
static int
@@ -1234,6 +1251,27 @@
}
/*
+ * Generate an ISN_LOADOUTER instruction
+ */
+ static int
+generate_LOADOUTER(
+ cctx_T *cctx,
+ int idx,
+ int nesting,
+ type_T *type)
+{
+ isn_T *isn;
+
+ RETURN_OK_IF_SKIP(cctx);
+ if ((isn = generate_instr_type(cctx, ISN_LOADOUTER, type)) == NULL)
+ return FAIL;
+ isn->isn_arg.outer.outer_idx = idx;
+ isn->isn_arg.outer.outer_depth = nesting;
+
+ return OK;
+}
+
+/*
* Generate an ISN_LOADV instruction for v:var.
*/
static int
@@ -1439,6 +1477,11 @@
isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
cctx->ctx_has_closure = 1;
+ // if the referenced function is a closure, it may use items further up in
+ // the nested context, including this one.
+ if (ufunc->uf_flags & FC_CLOSURE)
+ cctx->ctx_ufunc->uf_flags |= FC_CLOSURE;
+
if (ga_grow(stack, 1) == FAIL)
return FAIL;
((type_T **)stack->ga_data)[stack->ga_len] =
@@ -2589,7 +2632,7 @@
size_t len = end - *arg;
int idx;
int gen_load = FALSE;
- int gen_load_outer = FALSE;
+ int gen_load_outer = 0;
name = vim_strnsave(*arg, end - *arg);
if (name == NULL)
@@ -2597,7 +2640,7 @@
if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
{
- if (!gen_load_outer)
+ if (gen_load_outer == 0)
gen_load = TRUE;
}
else
@@ -2608,8 +2651,8 @@
{
type = lvar.lv_type;
idx = lvar.lv_idx;
- if (lvar.lv_from_outer)
- gen_load_outer = TRUE;
+ if (lvar.lv_from_outer != 0)
+ gen_load_outer = lvar.lv_from_outer;
else
gen_load = TRUE;
}
@@ -2631,9 +2674,9 @@
}
if (gen_load)
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
- if (gen_load_outer)
+ if (gen_load_outer > 0)
{
- res = generate_LOAD(cctx, ISN_LOADOUTER, idx, NULL, type);
+ res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
cctx->ctx_outer_used = TRUE;
}
}
@@ -5120,9 +5163,9 @@
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
- if (lvar->lv_from_outer)
- generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
- NULL, type);
+ if (lvar->lv_from_outer > 0)
+ generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
+ type);
else
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
break;
@@ -6178,7 +6221,7 @@
// optimization: turn "var = 123" from ISN_PUSHNR +
// ISN_STORE into ISN_STORENR
- if (!lhs.lhs_lvar->lv_from_outer
+ if (lhs.lhs_lvar->lv_from_outer == 0
&& instr->ga_len == instr_count + 1
&& isn->isn_type == ISN_PUSHNR)
{
@@ -6190,9 +6233,9 @@
if (stack->ga_len > 0)
--stack->ga_len;
}
- else if (lhs.lhs_lvar->lv_from_outer)
- generate_STORE(cctx, ISN_STOREOUTER,
- lhs.lhs_lvar->lv_idx, NULL);
+ else if (lhs.lhs_lvar->lv_from_outer > 0)
+ generate_STOREOUTER(cctx, lhs.lhs_lvar->lv_idx,
+ lhs.lhs_lvar->lv_from_outer);
else
generate_STORE(cctx, ISN_STORE, lhs.lhs_lvar->lv_idx, NULL);
}