changeset 20255:aac52c32a91f v8.2.0683

patch 8.2.0683: Vim9: parsing type does not always work Commit: https://github.com/vim/vim/commit/5adc55cb746893c6ddf7865ff654582902dee2e3 Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 2 23:12:58 2020 +0200 patch 8.2.0683: Vim9: parsing type does not always work 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.
author Bram Moolenaar <Bram@vim.org>
date Sat, 02 May 2020 23:15:03 +0200
parents c7a2968adc24
children e08857045ec1
files src/proto/vim9execute.pro src/testdir/test_vim9_func.vim src/userfunc.c src/version.c src/vim9compile.c src/vim9execute.c
diffstat 6 files changed, 84 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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 : */
--- 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
--- 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 *f
     {
 	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;
 }
--- 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,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -824,7 +824,7 @@ generate_TYPECHECK(cctx_T *cctx, type_T 
     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;
--- 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