Skip to content

Commit ab36052

Browse files
committed
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)
1 parent cff40ff commit ab36052

7 files changed

Lines changed: 177 additions & 43 deletions

File tree

src/structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,6 +1978,8 @@ struct partial_S
19781978
// For a compiled closure: the arguments and local variables.
19791979
garray_T *pt_ectx_stack; // where to find local vars
19801980
int pt_ectx_frame; // index of function frame in uf_ectx_stack
1981+
garray_T *pt_outer_stack; // pt_ectx_stack one level up
1982+
int pt_outer_frame; // pt_ectx_frame one level up.
19811983
funcstack_T *pt_funcstack; // copy of stack, used after context
19821984
// function returns
19831985

src/testdir/test_vim9_disassemble.vim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,17 +567,17 @@ def Test_disassemble_closure()
567567
var res = execute('disass g:Append')
568568
assert_match('<lambda>\d\_s*' ..
569569
'local ..= arg\_s*' ..
570-
'\d LOADOUTER $0\_s*' ..
570+
'\d LOADOUTER level 1 $0\_s*' ..
571571
'\d LOAD arg\[-1\]\_s*' ..
572572
'\d CONCAT\_s*' ..
573-
'\d STOREOUTER $0\_s*' ..
573+
'\d STOREOUTER level 1 $0\_s*' ..
574574
'\d RETURN 0',
575575
res)
576576

577577
res = execute('disass g:Get')
578578
assert_match('<lambda>\d\_s*' ..
579579
'return local\_s*' ..
580-
'\d LOADOUTER $0\_s*' ..
580+
'\d LOADOUTER level 1 $0\_s*' ..
581581
'\d RETURN',
582582
res)
583583

src/testdir/test_vim9_func.vim

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,16 @@ def Test_list_lambda()
18121812
assert_match('def <lambda>\d\+(_: any, ...): number\n1 return 0\n enddef', body)
18131813
enddef
18141814

1815+
def DoFilterThis(a: string): list<string>
1816+
# closure nested inside another closure using argument
1817+
var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0)
1818+
return ['x', 'y', 'a', 'x2', 'c']->Filter()
1819+
enddef
1820+
1821+
def Test_nested_closure_using_argument()
1822+
assert_equal(['x', 'x2'], DoFilterThis('x'))
1823+
enddef
1824+
18151825
func Test_silent_echo()
18161826
CheckScreendump
18171827

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2321,
753755
/**/
754756
2320,
755757
/**/

src/vim9.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ typedef enum {
3333
ISN_LOADWDICT, // push w: dict
3434
ISN_LOADTDICT, // push t: dict
3535
ISN_LOADS, // push s: variable isn_arg.loadstore
36-
ISN_LOADOUTER, // push variable from outer scope isn_arg.number
36+
ISN_LOADOUTER, // push variable from outer scope isn_arg.outer
3737
ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
3838
ISN_LOADOPT, // push option isn_arg.string
3939
ISN_LOADENV, // push environment variable isn_arg.string
@@ -47,7 +47,7 @@ typedef enum {
4747
ISN_STOREW, // pop into window-local variable isn_arg.string
4848
ISN_STORET, // pop into tab-local variable isn_arg.string
4949
ISN_STORES, // pop into script variable isn_arg.loadstore
50-
ISN_STOREOUTER, // pop variable into outer scope isn_arg.number
50+
ISN_STOREOUTER, // pop variable into outer scope isn_arg.outer
5151
ISN_STORESCRIPT, // pop into script variable isn_arg.script
5252
ISN_STOREOPT, // pop into option isn_arg.string
5353
ISN_STOREENV, // pop into environment variable isn_arg.string
@@ -303,6 +303,12 @@ typedef struct {
303303
int unp_semicolon; // last item gets list of remainder
304304
} unpack_T;
305305

306+
// arguments to ISN_LOADOUTER and ISN_STOREOUTER
307+
typedef struct {
308+
int outer_idx; // index
309+
int outer_depth; // nesting level, stack frames to go up
310+
} outer_T;
311+
306312
/*
307313
* Instruction
308314
*/
@@ -342,6 +348,7 @@ struct isn_S {
342348
put_T put;
343349
cmod_T cmdmod;
344350
unpack_T unpack;
351+
outer_T outer;
345352
} isn_arg;
346353
};
347354

src/vim9compile.c

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ typedef struct {
108108
char_u *lv_name;
109109
type_T *lv_type;
110110
int lv_idx; // index of the variable on the stack
111-
int lv_from_outer; // when TRUE using ctx_outer scope
111+
int lv_from_outer; // nesting level, using ctx_outer scope
112112
int lv_const; // when TRUE cannot be assigned to
113113
int lv_arg; // when TRUE this is an argument
114114
} lvar_T;
@@ -149,7 +149,7 @@ static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted);
149149

150150
/*
151151
* Lookup variable "name" in the local scope and return it in "lvar".
152-
* "lvar->lv_from_outer" is set accordingly.
152+
* "lvar->lv_from_outer" is incremented accordingly.
153153
* If "lvar" is NULL only check if the variable can be found.
154154
* Return FAIL if not found.
155155
*/
@@ -172,7 +172,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
172172
if (lvar != NULL)
173173
{
174174
*lvar = *lvp;
175-
lvar->lv_from_outer = FALSE;
175+
lvar->lv_from_outer = 0;
176176
}
177177
return OK;
178178
}
@@ -186,7 +186,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
186186
if (lvar != NULL)
187187
{
188188
cctx->ctx_outer_used = TRUE;
189-
lvar->lv_from_outer = TRUE;
189+
++lvar->lv_from_outer;
190190
}
191191
return OK;
192192
}
@@ -258,7 +258,7 @@ arg_exists(
258258
if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
259259
== OK)
260260
{
261-
*gen_load_outer = TRUE;
261+
++*gen_load_outer;
262262
return OK;
263263
}
264264
}
@@ -1175,6 +1175,23 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
11751175
return OK;
11761176
}
11771177

1178+
/*
1179+
* Generate an ISN_STOREOUTER instruction.
1180+
*/
1181+
static int
1182+
generate_STOREOUTER(cctx_T *cctx, int idx, int level)
1183+
{
1184+
isn_T *isn;
1185+
1186+
RETURN_OK_IF_SKIP(cctx);
1187+
if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
1188+
return FAIL;
1189+
isn->isn_arg.outer.outer_idx = idx;
1190+
isn->isn_arg.outer.outer_depth = level;
1191+
1192+
return OK;
1193+
}
1194+
11781195
/*
11791196
* Generate an ISN_STORENR instruction (short for ISN_PUSHNR + ISN_STORE)
11801197
*/
@@ -1233,6 +1250,27 @@ generate_LOAD(
12331250
return OK;
12341251
}
12351252

1253+
/*
1254+
* Generate an ISN_LOADOUTER instruction
1255+
*/
1256+
static int
1257+
generate_LOADOUTER(
1258+
cctx_T *cctx,
1259+
int idx,
1260+
int nesting,
1261+
type_T *type)
1262+
{
1263+
isn_T *isn;
1264+
1265+
RETURN_OK_IF_SKIP(cctx);
1266+
if ((isn = generate_instr_type(cctx, ISN_LOADOUTER, type)) == NULL)
1267+
return FAIL;
1268+
isn->isn_arg.outer.outer_idx = idx;
1269+
isn->isn_arg.outer.outer_depth = nesting;
1270+
1271+
return OK;
1272+
}
1273+
12361274
/*
12371275
* Generate an ISN_LOADV instruction for v:var.
12381276
*/
@@ -1439,6 +1477,11 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
14391477
isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
14401478
cctx->ctx_has_closure = 1;
14411479

1480+
// if the referenced function is a closure, it may use items further up in
1481+
// the nested context, including this one.
1482+
if (ufunc->uf_flags & FC_CLOSURE)
1483+
cctx->ctx_ufunc->uf_flags |= FC_CLOSURE;
1484+
14421485
if (ga_grow(stack, 1) == FAIL)
14431486
return FAIL;
14441487
((type_T **)stack->ga_data)[stack->ga_len] =
@@ -2589,15 +2632,15 @@ compile_load(
25892632
size_t len = end - *arg;
25902633
int idx;
25912634
int gen_load = FALSE;
2592-
int gen_load_outer = FALSE;
2635+
int gen_load_outer = 0;
25932636

25942637
name = vim_strnsave(*arg, end - *arg);
25952638
if (name == NULL)
25962639
return FAIL;
25972640

25982641
if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
25992642
{
2600-
if (!gen_load_outer)
2643+
if (gen_load_outer == 0)
26012644
gen_load = TRUE;
26022645
}
26032646
else
@@ -2608,8 +2651,8 @@ compile_load(
26082651
{
26092652
type = lvar.lv_type;
26102653
idx = lvar.lv_idx;
2611-
if (lvar.lv_from_outer)
2612-
gen_load_outer = TRUE;
2654+
if (lvar.lv_from_outer != 0)
2655+
gen_load_outer = lvar.lv_from_outer;
26132656
else
26142657
gen_load = TRUE;
26152658
}
@@ -2631,9 +2674,9 @@ compile_load(
26312674
}
26322675
if (gen_load)
26332676
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
2634-
if (gen_load_outer)
2677+
if (gen_load_outer > 0)
26352678
{
2636-
res = generate_LOAD(cctx, ISN_LOADOUTER, idx, NULL, type);
2679+
res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
26372680
cctx->ctx_outer_used = TRUE;
26382681
}
26392682
}
@@ -5120,9 +5163,9 @@ generate_loadvar(
51205163
generate_LOADV(cctx, name + 2, TRUE);
51215164
break;
51225165
case dest_local:
5123-
if (lvar->lv_from_outer)
5124-
generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
5125-
NULL, type);
5166+
if (lvar->lv_from_outer > 0)
5167+
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
5168+
type);
51265169
else
51275170
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
51285171
break;
@@ -6178,7 +6221,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
61786221

61796222
// optimization: turn "var = 123" from ISN_PUSHNR +
61806223
// ISN_STORE into ISN_STORENR
6181-
if (!lhs.lhs_lvar->lv_from_outer
6224+
if (lhs.lhs_lvar->lv_from_outer == 0
61826225
&& instr->ga_len == instr_count + 1
61836226
&& isn->isn_type == ISN_PUSHNR)
61846227
{
@@ -6190,9 +6233,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
61906233
if (stack->ga_len > 0)
61916234
--stack->ga_len;
61926235
}
6193-
else if (lhs.lhs_lvar->lv_from_outer)
6194-
generate_STORE(cctx, ISN_STOREOUTER,
6195-
lhs.lhs_lvar->lv_idx, NULL);
6236+
else if (lhs.lhs_lvar->lv_from_outer > 0)
6237+
generate_STOREOUTER(cctx, lhs.lhs_lvar->lv_idx,
6238+
lhs.lhs_lvar->lv_from_outer);
61966239
else
61976240
generate_STORE(cctx, ISN_STORE, lhs.lhs_lvar->lv_idx, NULL);
61986241
}

0 commit comments

Comments
 (0)