summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval.c6
-rw-r--r--src/structs.h1
-rw-r--r--src/testdir/test_vim9_disassemble.vim36
-rw-r--r--src/testdir/test_vim9_func.vim26
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h3
-rw-r--r--src/vim9compile.c13
-rw-r--r--src/vim9execute.c19
8 files changed, 97 insertions, 9 deletions
diff --git a/src/eval.c b/src/eval.c
index 72cb0897a..b7003a882 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -4337,9 +4337,11 @@ set_ref_in_item(
partial_T *pt = tv->vval.v_partial;
int i;
- // A partial does not have a copyID, because it cannot contain itself.
- if (pt != NULL)
+ if (pt != NULL && pt->pt_copyID != copyID)
{
+ // Didn't see this partial yet.
+ pt->pt_copyID = copyID;
+
abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL)
diff --git a/src/structs.h b/src/structs.h
index 7fd325a82..69233a967 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1812,6 +1812,7 @@ struct partial_S
typval_T *pt_argv; // arguments in allocated array
dict_T *pt_dict; // dict for "self"
+ int pt_copyID; // funcstack may contain pointer to partial
};
typedef struct AutoPatCmd_S AutoPatCmd;
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 4e2caf09f..11703416d 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -291,6 +291,42 @@ def Test_disassemble_call()
res)
enddef
+def s:CreateRefs()
+ let local = 'a'
+ def Append(arg: string)
+ local ..= arg
+ enddef
+ g:Append = Append
+ def Get(): string
+ return local
+ enddef
+ g:Get = Get
+enddef
+
+def Test_disassemble_closure()
+ CreateRefs()
+ let res = execute('disass g:Append')
+ assert_match('<lambda>\d.*' ..
+ 'local ..= arg.*' ..
+ '\d LOADOUTER $0.*' ..
+ '\d LOAD arg\[-1\].*' ..
+ '\d CONCAT.*' ..
+ '\d STOREOUTER $0.*' ..
+ '\d PUSHNR 0.*' ..
+ '\d RETURN.*',
+ res)
+
+ res = execute('disass g:Get')
+ assert_match('<lambda>\d.*' ..
+ 'return local.*' ..
+ '\d LOADOUTER $0.*' ..
+ '\d RETURN.*',
+ res)
+
+ unlet g:Append
+ unlet g:Get
+enddef
+
def EchoArg(arg: string): string
return arg
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 49dc2f2f2..83cef67c2 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -738,6 +738,32 @@ def Test_closure_using_argument()
unlet g:UseVararg
enddef
+def MakeGetAndAppendRefs()
+ let local = 'a'
+
+ def Append(arg: string)
+ local ..= arg
+ enddef
+ g:Append = Append
+
+ def Get(): string
+ return local
+ enddef
+ g:Get = Get
+enddef
+
+def Test_closure_append_get()
+ MakeGetAndAppendRefs()
+ assert_equal('a', g:Get())
+ g:Append('-b')
+ assert_equal('a-b', g:Get())
+ g:Append('-c')
+ assert_equal('a-b-c', g:Get())
+
+ unlet g:Append
+ unlet g:Get
+enddef
+
def Test_nested_closure()
let local = 'text'
def Closure(arg: string): string
diff --git a/src/version.c b/src/version.c
index 523791ca0..b507c89b2 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 703,
+/**/
702,
/**/
701,
diff --git a/src/vim9.h b/src/vim9.h
index aac776af1..709716ee3 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -40,8 +40,9 @@ typedef enum {
ISN_STOREW, // pop into window-local variable isn_arg.string
ISN_STORET, // pop into tab-local variable isn_arg.string
ISN_STORES, // pop into script variable isn_arg.loadstore
+ ISN_STOREOUTER, // pop variable into outer scope isn_arg.number
ISN_STORESCRIPT, // pop into script variable isn_arg.script
- ISN_STOREOPT, // pop into option isn_arg.string
+ ISN_STOREOPT, // pop into option isn_arg.string
ISN_STOREENV, // pop into environment variable isn_arg.string
ISN_STOREREG, // pop into register isn_arg.number
// ISN_STOREOTHER, // pop into other script variable isn_arg.other.
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 42fc0749a..5e29cc015 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4496,7 +4496,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
- generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
+ if (lvar->lv_from_outer)
+ generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
+ NULL, type);
+ else
+ generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
break;
}
}
@@ -4713,8 +4717,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
// optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE
// into ISN_STORENR
- if (instr->ga_len == instr_count + 1
- && isn->isn_type == ISN_PUSHNR)
+ if (!lvar->lv_from_outer && instr->ga_len == instr_count + 1
+ && isn->isn_type == ISN_PUSHNR)
{
varnumber_T val = isn->isn_arg.number;
garray_T *stack = &cctx->ctx_type_stack;
@@ -4725,6 +4729,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (stack->ga_len > 0)
--stack->ga_len;
}
+ else if (lvar->lv_from_outer)
+ generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx, NULL);
else
generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
}
@@ -6686,6 +6692,7 @@ delete_instr(isn_T *isn)
case ISN_PUSHSPEC:
case ISN_RETURN:
case ISN_STORE:
+ case ISN_STOREOUTER:
case ISN_STOREV:
case ISN_STORENR:
case ISN_STOREREG:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 5915ea1dc..9651f5e9b 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1070,6 +1070,14 @@ call_def_function(
*tv = *STACK_TV_BOT(0);
break;
+ // store variable or argument in outer scope
+ case ISN_STOREOUTER:
+ --ectx.ec_stack.ga_len;
+ tv = STACK_OUT_TV_VAR(iptr->isn_arg.number);
+ clear_tv(tv);
+ *tv = *STACK_TV_BOT(0);
+ break;
+
// store s: variable in old script
case ISN_STORES:
{
@@ -2133,7 +2141,7 @@ ex_disassemble(exarg_T *eap)
int is_global = FALSE;
fname = trans_function_name(&arg, &is_global, FALSE,
- TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
+ TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL);
if (fname == NULL)
{
semsg(_(e_invarg2), eap->arg);
@@ -2275,12 +2283,17 @@ ex_disassemble(exarg_T *eap)
break;
case ISN_STORE:
+ case ISN_STOREOUTER:
+ {
+ char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER";
+
if (iptr->isn_arg.number < 0)
- smsg("%4d STORE arg[%lld]", current,
+ smsg("%4d STORE%s arg[%lld]", current, add,
(long long)(iptr->isn_arg.number + STACK_FRAME_SIZE));
else
- smsg("%4d STORE $%lld", current,
+ smsg("%4d STORE%s $%lld", current, add,
(long long)(iptr->isn_arg.number));
+ }
break;
case ISN_STOREV:
smsg("%4d STOREV v:%s", current,