0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 8.2.0703: Vim9: closure cannot store value in outer context

Problem:    Vim9: closure cannot store value in outer context.
Solution:   Make storing value in outer context work.  Make :disassemble
            accept a function reference.
This commit is contained in:
Bram Moolenaar
2020-05-06 21:06:30 +02:00
parent 54ed0dff29
commit b68b346e6d
8 changed files with 97 additions and 9 deletions

View File

@@ -4337,9 +4337,11 @@ set_ref_in_item(
partial_T *pt = tv->vval.v_partial; partial_T *pt = tv->vval.v_partial;
int i; int i;
// A partial does not have a copyID, because it cannot contain itself. if (pt != NULL && pt->pt_copyID != copyID)
if (pt != NULL)
{ {
// Didn't see this partial yet.
pt->pt_copyID = copyID;
abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL) if (pt->pt_dict != NULL)

View File

@@ -1812,6 +1812,7 @@ struct partial_S
typval_T *pt_argv; // arguments in allocated array typval_T *pt_argv; // arguments in allocated array
dict_T *pt_dict; // dict for "self" dict_T *pt_dict; // dict for "self"
int pt_copyID; // funcstack may contain pointer to partial
}; };
typedef struct AutoPatCmd_S AutoPatCmd; typedef struct AutoPatCmd_S AutoPatCmd;

View File

@@ -291,6 +291,42 @@ def Test_disassemble_call()
res) res)
enddef 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 def EchoArg(arg: string): string
return arg return arg

View File

@@ -738,6 +738,32 @@ def Test_closure_using_argument()
unlet g:UseVararg unlet g:UseVararg
enddef 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() def Test_nested_closure()
let local = 'text' let local = 'text'
def Closure(arg: string): string def Closure(arg: string): string

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
703,
/**/ /**/
702, 702,
/**/ /**/

View File

@@ -40,8 +40,9 @@ typedef enum {
ISN_STOREW, // pop into window-local variable isn_arg.string ISN_STOREW, // pop into window-local variable isn_arg.string
ISN_STORET, // pop into tab-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_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_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_STOREENV, // pop into environment variable isn_arg.string
ISN_STOREREG, // pop into register isn_arg.number ISN_STOREREG, // pop into register isn_arg.number
// ISN_STOREOTHER, // pop into other script variable isn_arg.other. // ISN_STOREOTHER, // pop into other script variable isn_arg.other.

View File

@@ -4496,7 +4496,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
generate_LOADV(cctx, name + 2, TRUE); generate_LOADV(cctx, name + 2, TRUE);
break; break;
case dest_local: 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; 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 // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE
// into ISN_STORENR // into ISN_STORENR
if (instr->ga_len == instr_count + 1 if (!lvar->lv_from_outer && instr->ga_len == instr_count + 1
&& isn->isn_type == ISN_PUSHNR) && isn->isn_type == ISN_PUSHNR)
{ {
varnumber_T val = isn->isn_arg.number; varnumber_T val = isn->isn_arg.number;
garray_T *stack = &cctx->ctx_type_stack; 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) if (stack->ga_len > 0)
--stack->ga_len; --stack->ga_len;
} }
else if (lvar->lv_from_outer)
generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx, NULL);
else else
generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
} }
@@ -6686,6 +6692,7 @@ delete_instr(isn_T *isn)
case ISN_PUSHSPEC: case ISN_PUSHSPEC:
case ISN_RETURN: case ISN_RETURN:
case ISN_STORE: case ISN_STORE:
case ISN_STOREOUTER:
case ISN_STOREV: case ISN_STOREV:
case ISN_STORENR: case ISN_STORENR:
case ISN_STOREREG: case ISN_STOREREG:

View File

@@ -1070,6 +1070,14 @@ call_def_function(
*tv = *STACK_TV_BOT(0); *tv = *STACK_TV_BOT(0);
break; 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 // store s: variable in old script
case ISN_STORES: case ISN_STORES:
{ {
@@ -2133,7 +2141,7 @@ ex_disassemble(exarg_T *eap)
int is_global = FALSE; int is_global = FALSE;
fname = trans_function_name(&arg, &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) if (fname == NULL)
{ {
semsg(_(e_invarg2), eap->arg); semsg(_(e_invarg2), eap->arg);
@@ -2275,12 +2283,17 @@ ex_disassemble(exarg_T *eap)
break; break;
case ISN_STORE: case ISN_STORE:
case ISN_STOREOUTER:
{
char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER";
if (iptr->isn_arg.number < 0) 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)); (long long)(iptr->isn_arg.number + STACK_FRAME_SIZE));
else else
smsg("%4d STORE $%lld", current, smsg("%4d STORE%s $%lld", current, add,
(long long)(iptr->isn_arg.number)); (long long)(iptr->isn_arg.number));
}
break; break;
case ISN_STOREV: case ISN_STOREV:
smsg("%4d STOREV v:%s", current, smsg("%4d STOREV v:%s", current,