summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-05-02 23:12:58 +0200
committerBram Moolenaar <Bram@vim.org>2020-05-02 23:12:58 +0200
commit5adc55cb746893c6ddf7865ff654582902dee2e3 (patch)
tree7388ed33702564ad763cdd09af8dcb7552041925
parent1c0d44f8efae36c079cb8be395785827aa0b2737 (diff)
patch 8.2.0683: Vim9: parsing type does not always workv8.2.0683
Problem: Vim9: parsing type does not always work. Solution: Handle func type without return value. Test more closures. Fix type check offset. Fix garbage collection.
-rw-r--r--src/proto/vim9execute.pro3
-rw-r--r--src/testdir/test_vim9_func.vim44
-rw-r--r--src/userfunc.c5
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c11
-rw-r--r--src/vim9execute.c25
6 files changed, 84 insertions, 6 deletions
diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro
index 4f7262d741..578fe81e1b 100644
--- a/src/proto/vim9execute.pro
+++ b/src/proto/vim9execute.pro
@@ -1,6 +1,7 @@
/* vim9execute.c */
-int call_def_function(ufunc_T *ufunc, int argc, typval_T *argv, typval_T *rettv);
+int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, typval_T *rettv);
void ex_disassemble(exarg_T *eap);
int tv2bool(typval_T *tv);
int check_not_string(typval_T *tv);
+int set_ref_in_dfunc(ufunc_T *ufunc, int copyID);
/* vim: set ft=c : */
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index e1b3b7ffd6..a01e27c729 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -662,5 +662,49 @@ def Test_closure_ref_after_return()
unlet g:Ref
enddef
+def MakeTwoRefs()
+ let local = ['some']
+ g:Extend = {s -> local->add(s)}
+ g:Read = {-> local}
+enddef
+
+def Test_closure_two_refs()
+ MakeTwoRefs()
+ assert_equal('some', join(g:Read(), ' '))
+ g:Extend('more')
+ assert_equal('some more', join(g:Read(), ' '))
+ g:Extend('even')
+ assert_equal('some more even', join(g:Read(), ' '))
+
+ unlet g:Extend
+ unlet g:Read
+enddef
+
+" TODO: fix memory leak when using same function again.
+def MakeTwoRefs_2()
+ let local = ['some']
+ g:Extend = {s -> local->add(s)}
+ g:Read = {-> local}
+enddef
+
+def ReadRef(Ref: func(): list<string>): string
+ return join(Ref(), ' ')
+enddef
+
+def ExtendRef(Ref: func(string), add: string)
+ Ref(add)
+enddef
+
+def Test_closure_two_indirect_refs()
+ MakeTwoRefs_2()
+ assert_equal('some', ReadRef(g:Read))
+ ExtendRef(g:Extend, 'more')
+ assert_equal('some more', ReadRef(g:Read))
+ ExtendRef(g:Extend, 'even')
+ assert_equal('some more even', ReadRef(g:Read))
+
+ unlet g:Extend
+ unlet g:Read
+enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/userfunc.c b/src/userfunc.c
index 2493dfea0c..6aee377953 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -4392,6 +4392,8 @@ set_ref_in_functions(int copyID)
fp = HI2UF(hi);
if (!func_name_refcount(fp->uf_name))
abort = abort || set_ref_in_func(NULL, fp, copyID);
+ else if (fp->uf_dfunc_idx >= 0)
+ abort = abort || set_ref_in_dfunc(fp, copyID);
}
}
return abort;
@@ -4439,7 +4441,10 @@ set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
{
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped)
abort = abort || set_ref_in_funccal(fc, copyID);
+ if (fp->uf_dfunc_idx >= 0)
+ abort = abort || set_ref_in_dfunc(fp, copyID);
}
+
vim_free(tofree);
return abort;
}
diff --git a/src/version.c b/src/version.c
index 21cda476a5..ae0855405c 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 */
/**/
+ 683,
+/**/
682,
/**/
681,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index eb84e35909..a4a71de1f3 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -824,7 +824,7 @@ generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset)
isn->isn_arg.type.ct_off = offset;
// type becomes vartype
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = vartype;
+ ((type_T **)stack->ga_data)[stack->ga_len + offset] = vartype;
return OK;
}
@@ -1671,8 +1671,13 @@ skip_type(char_u *start)
if (*p == ',')
p = skipwhite(p + 1);
}
- if (*p == ')' && p[1] == ':')
- p = skip_type(skipwhite(p + 2));
+ if (*p == ')')
+ {
+ if (p[1] == ':')
+ p = skip_type(skipwhite(p + 2));
+ else
+ p = skipwhite(p + 1);
+ }
}
return p;
diff --git a/src/vim9execute.c b/src/vim9execute.c
index c27bcf0ab2..2404282f8a 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2437,11 +2437,12 @@ ex_disassemble(exarg_T *eap)
break;
case ISN_FUNCREF:
{
+ funcref_T *funcref = &iptr->isn_arg.funcref;
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
- + iptr->isn_arg.funcref.fr_func;
+ + funcref->fr_func;
smsg("%4d FUNCREF %s $%d", current, df->df_ufunc->uf_name,
- iptr->isn_arg.funcref.fr_var_idx + df->df_varcount);
+ funcref->fr_var_idx + dfunc->df_varcount);
}
break;
@@ -2675,5 +2676,25 @@ check_not_string(typval_T *tv)
return OK;
}
+/*
+ * Mark items in a def function as used.
+ */
+ int
+set_ref_in_dfunc(ufunc_T *ufunc, int copyID)
+{
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
+ int abort = FALSE;
+
+ if (dfunc->df_funcstack != NULL)
+ {
+ typval_T *stack = dfunc->df_funcstack->fs_ga.ga_data;
+ int idx;
+
+ for (idx = 0; idx < dfunc->df_funcstack->fs_ga.ga_len; ++idx)
+ abort = abort || set_ref_in_item(stack + idx, copyID, NULL, NULL);
+ }
+ return abort;
+}
+
#endif // FEAT_EVAL