Patch 9.0.0481

6 views
Skip to first unread message

Bram Moolenaar

unread,
Sep 16, 2022, 2:05:03 PM9/16/22
to vim...@googlegroups.com

Patch 9.0.0481
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.
Files: src/structs.h, src/vim9.h, src/userfunc.c, src/proto/userfunc.pro,
src/vim9cmds.c, src/proto/vim9cmds.pro, src/vim9compile.c,
src/vim9execute.c, src/proto/vim9execute.pro, src/vim9expr.c,
src/vim9instr.c, src/proto/vim9instr.pro,


*** ../vim-9.0.0480/src/structs.h 2022-09-16 12:10:00.073526252 +0100
--- src/structs.h 2022-09-16 17:12:24.079665221 +0100
***************
*** 1656,1662 ****

/*
* Structure to hold info for a user function.
! * When adding a field check copy_func().
*/
typedef struct
{
--- 1656,1662 ----

/*
* Structure to hold info for a user function.
! * When adding a field check copy_lambda_to_global_func().
*/
typedef struct
{
***************
*** 1741,1747 ****
#define FC_NOARGS 0x200 // no a: variables in lambda
#define FC_VIM9 0x400 // defined in vim9 script file
#define FC_CFUNC 0x800 // defined as Lua C func
! #define FC_COPY 0x1000 // copy of another function by copy_func()
#define FC_LAMBDA 0x2000 // one line "return {expr}"

#define MAX_FUNC_ARGS 20 // maximum number of function arguments
--- 1741,1748 ----
#define FC_NOARGS 0x200 // no a: variables in lambda
#define FC_VIM9 0x400 // defined in vim9 script file
#define FC_CFUNC 0x800 // defined as Lua C func
! #define FC_COPY 0x1000 // copy of another function by
! // copy_lambda_to_global_func()
#define FC_LAMBDA 0x2000 // one line "return {expr}"

#define MAX_FUNC_ARGS 20 // maximum number of function arguments
***************
*** 2096,2105 ****

typedef struct outer_S outer_T;
struct outer_S {
! garray_T *out_stack; // stack from outer scope
int out_frame_idx; // index of stack frame in out_stack
outer_T *out_up; // outer scope of outer scope or NULL
partial_T *out_up_partial; // partial owning out_up or NULL
};

struct partial_S
--- 2097,2113 ----

typedef struct outer_S outer_T;
struct outer_S {
! garray_T *out_stack; // stack from outer scope, or a copy
! // containing only arguments and local vars
int out_frame_idx; // index of stack frame in out_stack
outer_T *out_up; // outer scope of outer scope or NULL
partial_T *out_up_partial; // partial owning out_up or NULL
+
+ garray_T *out_loop_stack; // stack from outer scope, or a copy
+ // containing only vars inside the loop
+ short out_loop_var_idx; // first variable defined in a loop
+ // in out_loop_stack
+ short out_loop_var_count; // number of variables defined in a loop
};

struct partial_S
*** ../vim-9.0.0480/src/vim9.h 2022-09-15 17:19:30.022390551 +0100
--- src/vim9.h 2022-09-16 17:39:50.823126806 +0100
***************
*** 354,369 ****
int ul_forceit; // forceit flag
} unlet_T;

// arguments to ISN_FUNCREF
typedef struct {
! int fr_dfunc_idx; // function index for :def function
! char_u *fr_func_name; // function name for legacy function
} funcref_T;

// arguments to ISN_NEWFUNC
typedef struct {
! char_u *nf_lambda; // name of the lambda already defined
! char_u *nf_global; // name of the global function to be created
} newfunc_T;

// arguments to ISN_CHECKLEN
--- 354,382 ----
int ul_forceit; // forceit flag
} unlet_T;

+ // extra arguments for funcref_T
+ typedef struct {
+ char_u *fre_func_name; // function name for legacy function
+ short fre_loop_var_idx; // index of first variable inside loop
+ short fre_loop_var_count; // number of variables inside loop
+ } funcref_extra_T;
+
// arguments to ISN_FUNCREF
typedef struct {
! int fr_dfunc_idx; // function index for :def function
! funcref_extra_T *fr_extra; // optional extra information
} funcref_T;

// arguments to ISN_NEWFUNC
typedef struct {
! char_u *nfa_lambda; // name of the lambda already defined
! char_u *nfa_global; // name of the global function to be created
! short nfa_loop_var_idx; // index of first variable inside loop
! short nfa_loop_var_count; // number of variables inside loop
! } newfuncarg_T;
!
! typedef struct {
! newfuncarg_T *nf_arg;
} newfunc_T;

// arguments to ISN_CHECKLEN
***************
*** 401,406 ****
--- 414,421 ----
int outer_depth; // nesting level, stack frames to go up
} isn_outer_T;

+ #define OUTER_LOOP_DEPTH -9 // used for outer_depth for loop variables
+
// arguments to ISN_SUBSTITUTE
typedef struct {
char_u *subs_cmd; // :s command
***************
*** 677,682 ****
--- 692,698 ----
char_u *lv_name;
type_T *lv_type;
int lv_idx; // index of the variable on the stack
+ int lv_loop_idx; // index of first variable inside a loop or -1
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
*** ../vim-9.0.0480/src/userfunc.c 2022-09-16 12:10:00.073526252 +0100
--- src/userfunc.c 2022-09-16 14:49:10.530297517 +0100
***************
*** 2452,2458 ****
* This is for when a compiled function defines a global function.
*/
int
! copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
{
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
ufunc_T *fp = NULL;
--- 2452,2463 ----
* This is for when a compiled function defines a global function.
*/
int
! copy_lambda_to_global_func(
! char_u *lambda,
! char_u *global,
! short loop_var_idx,
! short loop_var_count,
! ectx_T *ectx)
{
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
ufunc_T *fp = NULL;
***************
*** 2519,2525 ****

if (pt == NULL)
goto failed;
! if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
{
vim_free(pt);
goto failed;
--- 2524,2531 ----

if (pt == NULL)
goto failed;
! if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
! ectx) == FAIL)
{
vim_free(pt);
goto failed;
*** ../vim-9.0.0480/src/proto/userfunc.pro 2022-09-07 21:30:40.143379043 +0100
--- src/proto/userfunc.pro 2022-09-16 14:59:41.808573778 +0100
***************
*** 16,22 ****
int func_requires_g_prefix(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
void func_clear_free(ufunc_T *fp, int force);
! int copy_func(char_u *lambda, char_u *global, ectx_T *ectx);
int funcdepth_increment(void);
void funcdepth_decrement(void);
int funcdepth_get(void);
--- 16,22 ----
int func_requires_g_prefix(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
void func_clear_free(ufunc_T *fp, int force);
! int copy_lambda_to_global_func(char_u *lambda, char_u *global, short loop_var_idx, short loop_var_count, ectx_T *ectx);
int funcdepth_increment(void);
void funcdepth_decrement(void);
int funcdepth_get(void);
*** ../vim-9.0.0480/src/vim9cmds.c 2022-09-15 17:19:30.022390551 +0100
--- src/vim9cmds.c 2022-09-16 17:42:19.746693534 +0100
***************
*** 1246,1251 ****
--- 1246,1294 ----
}

/*
+ * Get the current information about variables declared inside a loop.
+ * Returns zero if there are none, otherwise the count.
+ * "loop_var_idx" is then set to the index of the first variable.
+ */
+ short
+ get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
+ {
+ scope_T *scope = cctx->ctx_scope;
+ int start_local_count;
+
+ while (scope != NULL && scope->se_type != WHILE_SCOPE
+ && scope->se_type != FOR_SCOPE)
+ scope = scope->se_outer;
+ if (scope == NULL)
+ return 0;
+
+ if (scope->se_type == WHILE_SCOPE)
+ start_local_count = scope->se_u.se_while.ws_local_count;
+ else
+ start_local_count = scope->se_u.se_for.fs_local_count;
+ if (cctx->ctx_locals.ga_len > start_local_count)
+ {
+ *loop_var_idx = (short)start_local_count;
+ return (short)(cctx->ctx_locals.ga_len - start_local_count);
+ }
+ return 0;
+ }
+
+ /*
+ * Get the index of the first variable in a loop, if any.
+ * Returns -1 if none.
+ */
+ int
+ get_loop_var_idx(cctx_T *cctx)
+ {
+ short loop_var_idx;
+
+ if (get_loop_var_info(cctx, &loop_var_idx) > 0)
+ return loop_var_idx;
+ return -1;
+ }
+
+ /*
* compile "continue"
*/
char_u *
*** ../vim-9.0.0480/src/proto/vim9cmds.pro 2022-09-04 15:40:31.816188110 +0100
--- src/proto/vim9cmds.pro 2022-09-16 17:43:16.122532065 +0100
***************
*** 11,16 ****
--- 11,18 ----
char_u *compile_endfor(char_u *arg, cctx_T *cctx);
char_u *compile_while(char_u *arg, cctx_T *cctx);
char_u *compile_endwhile(char_u *arg, cctx_T *cctx);
+ short get_loop_var_info(cctx_T *cctx, short *loop_var_idx);
+ int get_loop_var_idx(cctx_T *cctx);
char_u *compile_continue(char_u *arg, cctx_T *cctx);
char_u *compile_break(char_u *arg, cctx_T *cctx);
char_u *compile_block(char_u *arg, cctx_T *cctx);
*** ../vim-9.0.0480/src/vim9compile.c 2022-09-15 17:19:30.026390537 +0100
--- src/vim9compile.c 2022-09-16 17:50:06.701384676 +0100
***************
*** 54,59 ****
--- 54,60 ----
{
*lvar = *lvp;
lvar->lv_from_outer = 0;
+ lvar->lv_loop_idx = get_loop_var_idx(cctx);
}
return OK;
}
***************
*** 954,960 ****
// recursive call.
if (is_global)
{
! r = generate_NEWFUNC(cctx, lambda_name, func_name);
func_name = NULL;
lambda_name = NULL;
}
--- 955,962 ----
// recursive call.
if (is_global)
{
! // TODO: loop variable index and count
! r = generate_NEWFUNC(cctx, lambda_name, func_name, 0, 0);
func_name = NULL;
lambda_name = NULL;
}
***************
*** 1193,1199 ****
{
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);
}
--- 1195,1201 ----
{
if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
! lvar->lv_loop_idx, type);
else
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
}
*** ../vim-9.0.0480/src/vim9execute.c 2022-09-15 17:19:30.026390537 +0100
--- src/vim9execute.c 2022-09-16 18:19:16.005146029 +0100
***************
*** 673,678 ****
--- 673,681 ----
if (closure_count == 0)
return OK; // no funcrefs created

+ // Compute "top": the first entry in the stack used by the function.
+ // This is the first argument (after that comes the stack frame and then
+ // the local variables).
argcount = ufunc_argcount(dfunc->df_ufunc);
top = ectx->ec_frame_idx - argcount;

***************
*** 740,745 ****
--- 743,749 ----
else
copy_tv(tv, stack + idx);
}
+ // Skip the stack frame.
// Move the local variables.
for (idx = 0; idx < dfunc->df_varcount; ++idx)
{
***************
*** 770,779 ****
--- 774,790 ----
- closure_count + idx];
if (pt->pt_refcount > 1)
{
+ int prev_frame_idx = pt->pt_outer.out_frame_idx;
+
++funcstack->fs_refcount;
pt->pt_funcstack = funcstack;
pt->pt_outer.out_stack = &funcstack->fs_ga;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top;
+
+ // TODO: drop this, should be done at ISN_ENDLOOP
+ pt->pt_outer.out_loop_stack = &funcstack->fs_ga;
+ pt->pt_outer.out_loop_var_idx -=
+ prev_frame_idx - pt->pt_outer.out_frame_idx;
}
}
}
***************
*** 1814,1820 ****
* needed, especially when it is used as a closure.
*/
int
! fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
{
pt->pt_func = ufunc;
pt->pt_refcount = 1;
--- 1825,1836 ----
* needed, especially when it is used as a closure.
*/
int
! fill_partial_and_closure(
! partial_T *pt,
! ufunc_T *ufunc,
! short loop_var_idx,
! short loop_var_count,
! ectx_T *ectx)
{
pt->pt_func = ufunc;
pt->pt_refcount = 1;
***************
*** 1839,1844 ****
--- 1855,1868 ----
}
}

+ // The closure may need to find variables defined inside a loop. A
+ // new reference is made every time, ISN_ENDLOOP will check if they
+ // are actually used.
+ pt->pt_outer.out_loop_stack = &ectx->ec_stack;
+ pt->pt_outer.out_loop_var_idx = ectx->ec_frame_idx + STACK_FRAME_SIZE
+ + loop_var_idx;
+ pt->pt_outer.out_loop_var_count = loop_var_count;
+
// 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.
***************
*** 1853,1860 ****
++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
+ STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;

! ((partial_T **)ectx->ec_funcrefs.ga_data)
! [ectx->ec_funcrefs.ga_len] = pt;
++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len;
}
--- 1877,1884 ----
++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
+ STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;

! ((partial_T **)ectx->ec_funcrefs.ga_data)[ectx->ec_funcrefs.ga_len]
! = pt;
++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len;
}
***************
*** 3610,3618 ****
iemsg("LOADOUTER depth more than scope levels");
goto theend;
}
! tv = ((typval_T *)outer->out_stack->ga_data)
! + outer->out_frame_idx + STACK_FRAME_SIZE
! + iptr->isn_arg.outer.outer_idx;
if (iptr->isn_type == ISN_LOADOUTER)
{
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
--- 3634,3648 ----
iemsg("LOADOUTER depth more than scope levels");
goto theend;
}
! if (depth == OUTER_LOOP_DEPTH)
! // variable declared in loop
! tv = ((typval_T *)outer->out_loop_stack->ga_data)
! + outer->out_loop_var_idx
! + iptr->isn_arg.outer.outer_idx;
! else
! tv = ((typval_T *)outer->out_stack->ga_data)
! + outer->out_frame_idx + STACK_FRAME_SIZE
! + iptr->isn_arg.outer.outer_idx;
if (iptr->isn_type == ISN_LOADOUTER)
{
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
***************
*** 3913,3921 ****
// push a partial, a reference to a compiled function
case ISN_FUNCREF:
{
! partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
! ufunc_T *ufunc;
! funcref_T *funcref = &iptr->isn_arg.funcref;

if (pt == NULL)
goto theend;
--- 3943,3952 ----
// push a partial, a reference to a compiled function
case ISN_FUNCREF:
{
! partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
! ufunc_T *ufunc;
! funcref_T *funcref = &iptr->isn_arg.funcref;
! funcref_extra_T *extra = funcref->fr_extra;

if (pt == NULL)
goto theend;
***************
*** 3924,3930 ****
vim_free(pt);
goto theend;
}
! if (funcref->fr_func_name == NULL)
{
dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
--- 3955,3961 ----
vim_free(pt);
goto theend;
}
! if (extra == NULL || extra->fre_func_name == NULL)
{
dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
***************
*** 3932,3947 ****
ufunc = pt_dfunc->df_ufunc;
}
else
! {
! ufunc = find_func(funcref->fr_func_name, FALSE);
! }
if (ufunc == NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
iemsg("ufunc unexpectedly NULL for FUNCREF");
goto theend;
}
! if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
goto theend;
tv = STACK_TV_BOT(0);
++ectx->ec_stack.ga_len;
--- 3963,3979 ----
ufunc = pt_dfunc->df_ufunc;
}
else
! ufunc = find_func(extra->fre_func_name, FALSE);
if (ufunc == NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
iemsg("ufunc unexpectedly NULL for FUNCREF");
goto theend;
}
! if (fill_partial_and_closure(pt, ufunc,
! extra == NULL ? 0 : extra->fre_loop_var_idx,
! extra == NULL ? 0 : extra->fre_loop_var_count,
! ectx) == FAIL)
goto theend;
tv = STACK_TV_BOT(0);
++ectx->ec_stack.ga_len;
***************
*** 3954,3963 ****
// Create a global function from a lambda.
case ISN_NEWFUNC:
{
! newfunc_T *newfunc = &iptr->isn_arg.newfunc;

! if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
! ectx) == FAIL)
goto theend;
}
break;
--- 3986,3996 ----
// Create a global function from a lambda.
case ISN_NEWFUNC:
{
! newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;

! if (copy_lambda_to_global_func(arg->nfa_lambda,
! arg->nfa_global, arg->nfa_loop_var_idx,
! arg->nfa_loop_var_count, ectx) == FAIL)
goto theend;
}
break;
***************
*** 5520,5526 ****
ufunc_T *base_ufunc = dfunc->df_ufunc;

// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
! // by copy_func().
if (partial != NULL || base_ufunc->uf_partial != NULL)
{
ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T);
--- 5553,5559 ----
ufunc_T *base_ufunc = dfunc->df_ufunc;

// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
! // by copy_lambda_to_global_func().
if (partial != NULL || base_ufunc->uf_partial != NULL)
{
ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T);
***************
*** 5880,5894 ****
break;
case ISN_LOADOUTER:
{
! if (iptr->isn_arg.outer.outer_idx < 0)
smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
! iptr->isn_arg.outer.outer_depth,
! iptr->isn_arg.outer.outer_idx
+ STACK_FRAME_SIZE);
else
smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
! iptr->isn_arg.outer.outer_depth,
! iptr->isn_arg.outer.outer_idx);
}
break;
case ISN_LOADV:
--- 5913,5932 ----
break;
case ISN_LOADOUTER:
{
! isn_outer_T *outer = &iptr->isn_arg.outer;
!
! if (outer->outer_idx < 0)
smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
! outer->outer_depth,
! outer->outer_idx
+ STACK_FRAME_SIZE);
+ else if (outer->outer_depth == OUTER_LOOP_DEPTH)
+ smsg("%s%4d LOADOUTER level 1 $%d in loop",
+ pfx, current, outer->outer_idx);
else
smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
! outer->outer_depth,
! outer->outer_idx);
}
break;
case ISN_LOADV:
***************
*** 5971,5979 ****
iptr->isn_arg.number);
break;
case ISN_STOREOUTER:
! smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
! iptr->isn_arg.outer.outer_depth,
! iptr->isn_arg.outer.outer_idx);
break;
case ISN_STOREV:
smsg("%s%4d STOREV v:%s", pfx, current,
--- 6009,6024 ----
iptr->isn_arg.number);
break;
case ISN_STOREOUTER:
! {
! isn_outer_T *outer = &iptr->isn_arg.outer;
!
! if (outer->outer_depth == OUTER_LOOP_DEPTH)
! smsg("%s%4d STOREOUTER level 1 $%d in loop",
! pfx, current, outer->outer_idx);
! else
! smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
! outer->outer_depth, outer->outer_idx);
! }
break;
case ISN_STOREV:
smsg("%s%4d STOREV v:%s", pfx, current,
***************
*** 6190,6216 ****
break;
case ISN_FUNCREF:
{
! funcref_T *funcref = &iptr->isn_arg.funcref;
! char_u *name;

! if (funcref->fr_func_name == NULL)
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
name = df->df_ufunc->uf_name;
}
else
! name = funcref->fr_func_name;
! smsg("%s%4d FUNCREF %s", pfx, current, name);
}
break;

case ISN_NEWFUNC:
{
! newfunc_T *newfunc = &iptr->isn_arg.newfunc;

! smsg("%s%4d NEWFUNC %s %s", pfx, current,
! newfunc->nf_lambda, newfunc->nf_global);
}
break;

--- 6235,6275 ----
break;
case ISN_FUNCREF:
{
! funcref_T *funcref = &iptr->isn_arg.funcref;
! funcref_extra_T *extra = funcref->fr_extra;
! char_u *name;

! if (extra == NULL || extra->fre_func_name == NULL)
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
name = df->df_ufunc->uf_name;
}
else
! name = extra->fre_func_name;
! if (extra == NULL || extra->fre_loop_var_count == 0)
! smsg("%s%4d FUNCREF %s", pfx, current, name);
! else
! smsg("%s%4d FUNCREF %s var $%d - $%d", pfx, current,
! name,
! extra->fre_loop_var_idx,
! extra->fre_loop_var_idx
! + extra->fre_loop_var_count - 1);
}
break;

case ISN_NEWFUNC:
{
! newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;

! if (arg->nfa_loop_var_count == 0)
! smsg("%s%4d NEWFUNC %s %s", pfx, current,
! arg->nfa_lambda, arg->nfa_global);
! else
! smsg("%s%4d NEWFUNC %s %s var $%d - $%d", pfx, current,
! arg->nfa_lambda, arg->nfa_global,
! arg->nfa_loop_var_idx,
! arg->nfa_loop_var_idx + arg->nfa_loop_var_count - 1);
}
break;

*** ../vim-9.0.0480/src/proto/vim9execute.pro 2022-09-07 16:48:41.183678514 +0100
--- src/proto/vim9execute.pro 2022-09-16 14:38:42.748130110 +0100
***************
*** 9,15 ****
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
char_u *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
! int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
int may_load_script(int sid, int *loaded);
typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc);
--- 9,15 ----
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
char_u *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
! int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, short loop_var_idx, short loop_var_count, ectx_T *ectx);
int may_load_script(int sid, int *loaded);
typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc);
*** ../vim-9.0.0480/src/vim9expr.c 2022-09-11 11:49:19.098228660 +0100
--- src/vim9expr.c 2022-09-16 17:48:36.265634058 +0100
***************
*** 496,501 ****
--- 496,502 ----
int idx;
int gen_load = FALSE;
int gen_load_outer = 0;
+ int outer_loop_idx = -1;

name = vim_strnsave(*arg, end - *arg);
if (name == NULL)
***************
*** 520,525 ****
--- 521,527 ----
{
type = lvar.lv_type;
idx = lvar.lv_idx;
+ outer_loop_idx = lvar.lv_loop_idx;
if (lvar.lv_from_outer != 0)
gen_load_outer = lvar.lv_from_outer;
else
***************
*** 544,550 ****
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
if (gen_load_outer > 0)
{
! res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
cctx->ctx_outer_used = TRUE;
}
}
--- 546,553 ----
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
if (gen_load_outer > 0)
{
! res = generate_LOADOUTER(cctx, idx,
! gen_load_outer, outer_loop_idx, type);
cctx->ctx_outer_used = TRUE;
}
}
*** ../vim-9.0.0480/src/vim9instr.c 2022-09-15 17:19:30.026390537 +0100
--- src/vim9instr.c 2022-09-16 18:52:31.835791137 +0100
***************
*** 916,930 ****
* 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;
}
--- 916,940 ----
* Generate an ISN_STOREOUTER instruction.
*/
static int
! 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;
! 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,1004 ****
--- 1009,1015 ----
cctx_T *cctx,
int idx,
int nesting,
+ int loop_idx,
type_T *type)
{
isn_T *isn;
***************
*** 1006,1013 ****
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;

return OK;
}
--- 1017,1034 ----
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
return FAIL;
! 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,1205 ****
/*
* Generate an ISN_FUNCREF instruction.
* "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
*/
int
! generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
{
! isn_T *isn;
! type_T *type;

RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL;
if (isnp != NULL)
*isnp = isn;
if (ufunc->uf_def_status == UF_NOT_COMPILED)
! isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
else
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
cctx->ctx_has_closure = 1;
--- 1207,1245 ----
/*
* 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)
{
! 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)
! 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,1227 ****
* consumed.
*/
int
! generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
{
isn_T *isn;
int ret = OK;
--- 1261,1272 ----
* consumed.
*/
int
! 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,1240 ****
ret = FAIL;
else
{
! isn->isn_arg.newfunc.nf_lambda = lambda_name;
! isn->isn_arg.newfunc.nf_global = func_name;
! return OK;
}
}
vim_free(lambda_name);
--- 1277,1295 ----
ret = FAIL;
else
{
! 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,2129 ****
}
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);
}
--- 2178,2184 ----
}
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_loop_idx);
else
generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
}
***************
*** 2226,2247 ****

case ISN_FUNCREF:
{
! if (isn->isn_arg.funcref.fr_func_name == NULL)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
! + isn->isn_arg.funcref.fr_dfunc_idx;
ufunc_T *ufunc = dfunc->df_ufunc;

if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
func_ptr_unref(ufunc);
}
! else
{
! char_u *name = isn->isn_arg.funcref.fr_func_name;

if (name != NULL)
func_unref(name);
! vim_free(isn->isn_arg.funcref.fr_func_name);
}
}
break;
--- 2281,2308 ----

case ISN_FUNCREF:
{
! 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)
! + funcref->fr_dfunc_idx;
ufunc_T *ufunc = dfunc->df_ufunc;

if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
func_ptr_unref(ufunc);
}
! if (extra != NULL)
{
! char_u *name = extra->fre_func_name;

if (name != NULL)
+ {
func_unref(name);
! vim_free(name);
! }
! vim_free(extra);
}
}
break;
***************
*** 2259,2275 ****

case ISN_NEWFUNC:
{
! char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
! ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);

! if (ufunc != NULL)
{
! unlink_def_function(ufunc);
! func_ptr_unref(ufunc);
! }

! vim_free(lambda);
! vim_free(isn->isn_arg.newfunc.nf_global);
}
break;

--- 2320,2342 ----

case ISN_NEWFUNC:
{
! newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;

! if (arg != NULL)
{
! ufunc_T *ufunc = find_func_even_dead(
! arg->nfa_lambda, FFED_IS_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;

*** ../vim-9.0.0480/src/proto/vim9instr.pro 2022-09-15 17:19:30.026390537 +0100
--- src/proto/vim9instr.pro 2022-09-16 17:51:45.897112726 +0100
***************
*** 31,37 ****
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
! int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, type_T *type);
int generate_LOADV(cctx_T *cctx, char_u *name);
int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
int generate_LOCKCONST(cctx_T *cctx);
--- 31,37 ----
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
! int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_idx, type_T *type);
int generate_LOADV(cctx_T *cctx, char_u *name);
int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
int generate_LOCKCONST(cctx_T *cctx);
***************
*** 40,46 ****
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
! int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
int generate_WHILE(cctx_T *cctx, int funcref_idx);
--- 40,46 ----
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
! int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name, short loop_var_idx, short loop_var_count);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
int generate_WHILE(cctx_T *cctx, int funcref_idx);
*** ../vim-9.0.0480/src/version.c 2022-09-16 16:06:29.066260406 +0100
--- src/version.c 2022-09-16 19:02:02.158253404 +0100
***************
*** 705,706 ****
--- 705,708 ----
{ /* Add new patch number below this line */
+ /**/
+ 481,
/**/

--
The sooner you fall behind, the more time you'll have to catch up.

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages