Skip to content

Commit

Permalink
patch 8.2.3435: Vim9: dict is not passed to dict function
Browse files Browse the repository at this point in the history
Problem:    Vim9: dict is not passed to dict function.
Solution:   Keep the dict used until a function call.
  • Loading branch information
brammool committed Sep 13, 2021
1 parent 28e591d commit b1b6f4d
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 26 deletions.
35 changes: 34 additions & 1 deletion src/testdir/test_vim9_disassemble.vim
Expand Up @@ -412,7 +412,8 @@ def Test_disassemble_store_index()
'\d PUSHNR 0\_s*' ..
'\d LOAD $0\_s*' ..
'\d MEMBER dd\_s*' ..
'\d STOREINDEX any\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STOREINDEX any\_s*' ..
'\d\+ RETURN void',
res)
enddef
Expand Down Expand Up @@ -1625,11 +1626,13 @@ def Test_disassemble_dict_member()
'var res = d.item\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ MEMBER item\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STORE $1\_s*' ..
'res = d\["item"\]\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ PUSHS "item"\_s*' ..
'\d\+ MEMBER\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STORE $1\_s*',
instr)
assert_equal(1, DictMember())
Expand Down Expand Up @@ -2302,6 +2305,35 @@ def Test_debug_elseif()
res)
enddef

func Legacy() dict
echo 'legacy'
endfunc

def s:UseMember()
var d = {func: Legacy}
var v = d.func()
enddef

def Test_disassemble_dict_stack()
var res = execute('disass s:UseMember')
assert_match('<SNR>\d*_UseMember\_s*' ..
'var d = {func: Legacy}\_s*' ..
'\d PUSHS "func"\_s*' ..
'\d PUSHFUNC "Legacy"\_s*' ..
'\d NEWDICT size 1\_s*' ..
'\d STORE $0\_s*' ..

'var v = d.func()\_s*' ..
'\d LOAD $0\_s*' ..
'\d MEMBER func\_s*' ..
'\d PCALL top (argc 0)\_s*' ..
'\d PCALL end\_s*' ..
'\d CLEARDICT\_s*' ..
'\d\+ STORE $1\_s*' ..
'\d\+ RETURN void*',
res)
enddef

def s:EchoMessages()
echohl ErrorMsg | echom v:exception | echohl NONE
enddef
Expand Down Expand Up @@ -2363,4 +2395,5 @@ def Test_disassemble_after_reload()
enddef



" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
31 changes: 31 additions & 0 deletions src/testdir/test_vim9_func.vim
Expand Up @@ -2557,6 +2557,37 @@ def Test_legacy_errors()
endfor
enddef

def Test_call_legacy_with_dict()
var lines =<< trim END
vim9script
func Legacy() dict
let g:result = self.value
endfunc
def TestDirect()
var d = {value: 'yes', func: Legacy}
d.func()
enddef
TestDirect()
assert_equal('yes', g:result)
unlet g:result

def TestIndirect()
var d = {value: 'foo', func: Legacy}
var Fi = d.func
Fi()
enddef
TestIndirect()
assert_equal('foo', g:result)
unlet g:result

var d = {value: 'bar', func: Legacy}
d.func()
assert_equal('bar', g:result)
unlet g:result
END
CheckScriptSuccess(lines)
enddef

def DoFilterThis(a: string): list<string>
# closure nested inside another closure using argument
var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0)
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -755,6 +755,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3435,
/**/
3434,
/**/
Expand Down
3 changes: 3 additions & 0 deletions src/vim9.h
Expand Up @@ -162,6 +162,9 @@ typedef enum {
ISN_CHECKLEN, // check list length is isn_arg.checklen.cl_min_len
ISN_SETTYPE, // set dict type to isn_arg.type.ct_type

ISN_CLEARDICT, // clear dict saved by ISN_MEMBER/ISN_STRINGMEMBER
ISN_USEDICT, // use or clear dict saved by ISN_MEMBER/ISN_STRINGMEMBER

ISN_PUT, // ":put", uses isn_arg.put

ISN_CMDMOD, // set cmdmod
Expand Down
39 changes: 33 additions & 6 deletions src/vim9compile.c
Expand Up @@ -2878,9 +2878,10 @@ clear_ppconst(ppconst_T *ppconst)
/*
* Compile getting a member from a list/dict/string/blob. Stack has the
* indexable value and the index or the two indexes of a slice.
* "keeping_dict" is used for dict[func](arg) to pass dict to func.
*/
static int
compile_member(int is_slice, cctx_T *cctx)
compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
{
type_T **typep;
garray_T *stack = &cctx->ctx_type_stack;
Expand Down Expand Up @@ -2935,6 +2936,8 @@ compile_member(int is_slice, cctx_T *cctx)
return FAIL;
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
return FAIL;
if (keeping_dict != NULL)
*keeping_dict = TRUE;
}
else if (vartype == VAR_STRING)
{
Expand Down Expand Up @@ -4314,6 +4317,7 @@ compile_subscript(
ppconst_T *ppconst)
{
char_u *name_start = *end_leader;
int keeping_dict = FALSE;

for (;;)
{
Expand Down Expand Up @@ -4360,6 +4364,12 @@ compile_subscript(
return FAIL;
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
return FAIL;
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
}
else if (*p == '-' && p[1] == '>')
{
Expand Down Expand Up @@ -4470,6 +4480,12 @@ compile_subscript(
if (compile_call(arg, p - *arg, cctx, ppconst, 1) == FAIL)
return FAIL;
}
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
}
else if (**arg == '[')
{
Expand Down Expand Up @@ -4537,7 +4553,13 @@ compile_subscript(
}
*arg = *arg + 1;

if (compile_member(is_slice, cctx) == FAIL)
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
if (compile_member(is_slice, &keeping_dict, cctx) == FAIL)
return FAIL;
}
else if (*p == '.' && p[1] != '.')
Expand All @@ -4562,18 +4584,21 @@ compile_subscript(
semsg(_(e_syntax_error_at_str), *arg);
return FAIL;
}
if (keeping_dict && generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
if (generate_STRINGMEMBER(cctx, *arg, p - *arg) == FAIL)
return FAIL;
keeping_dict = TRUE;
*arg = p;
}
else
break;
}

// TODO - see handle_subscript():
// Turn "dict.Func" into a partial for "Func" bound to "dict".
// Don't do this when "Func" is already a partial that was bound
// explicitly (pt_auto is FALSE).
// This needs to be done at runtime to be able to check the type.
if (keeping_dict && generate_instr(cctx, ISN_USEDICT) == NULL)
return FAIL;

return OK;
}
Expand Down Expand Up @@ -6661,7 +6686,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
}

// Get the member.
if (compile_member(FALSE, cctx) == FAIL)
if (compile_member(FALSE, NULL, cctx) == FAIL)
return FAIL;
}
return OK;
Expand Down Expand Up @@ -10406,6 +10431,7 @@ delete_instr(isn_T *isn)
case ISN_CEXPR_AUCMD:
case ISN_CHECKLEN:
case ISN_CHECKNR:
case ISN_CLEARDICT:
case ISN_CMDMOD_REV:
case ISN_COMPAREANY:
case ISN_COMPAREBLOB:
Expand Down Expand Up @@ -10482,6 +10508,7 @@ delete_instr(isn_T *isn)
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
case ISN_UNPACK:
case ISN_USEDICT:
// nothing allocated
break;
}
Expand Down

0 comments on commit b1b6f4d

Please sign in to comment.