summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval.c3
-rw-r--r--src/evalfunc.c5
-rw-r--r--src/structs.h3
-rw-r--r--src/testdir/test_vim9_func.vim19
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c27
6 files changed, 54 insertions, 5 deletions
diff --git a/src/eval.c b/src/eval.c
index 7ab05be12..2942d0fe1 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -4526,6 +4526,9 @@ partial_free(partial_T *pt)
// "out_up" is no longer used, decrement refcount on partial that owns it.
partial_unref(pt->pt_outer.out_up_partial);
+ // Using pt_outer from another partial.
+ partial_unref(pt->pt_outer_partial);
+
// Decrease the reference count for the context of a closure. If down
// to the minimum it may be time to free it.
if (pt->pt_funcstack != NULL)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index eb12e75d0..b031369ef 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -4456,7 +4456,10 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
}
if (arg_pt != NULL)
- pt->pt_outer = arg_pt->pt_outer;
+ {
+ pt->pt_outer_partial = arg_pt;
+ ++arg_pt->pt_refcount;
+ }
}
rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = pt;
diff --git a/src/structs.h b/src/structs.h
index ecab3541d..1e759f5df 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2051,6 +2051,9 @@ struct partial_S
// For a compiled closure: the arguments and local variables scope
outer_T pt_outer;
+ // For a partial of a partial: use pt_outer values of this partial.
+ partial_T *pt_outer_partial;
+
funcstack_T *pt_funcstack; // copy of stack, used after context
// function returns
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 7ab15f72f..4ac4643e0 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -3477,6 +3477,25 @@ def Test_nested_closure_funcref()
unlet g:result_one g:result_two
enddef
+def Test_nested_closure_in_dict()
+ var lines =<< trim END
+ vim9script
+ def Func(): dict<any>
+ var n: number
+ def Inc(): number
+ ++n
+ return n
+ enddef
+ return {inc: function(Inc)}
+ enddef
+ disas Func
+ var d = Func()
+ assert_equal(1, d.inc())
+ assert_equal(2, d.inc())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
def Test_check_func_arg_types()
var lines =<< trim END
vim9script
diff --git a/src/version.c b/src/version.c
index 97bcfdef9..c1f90cdd0 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 */
/**/
+ 4322,
+/**/
4321,
/**/
4320,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 961e4507c..1412d0836 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -235,6 +235,23 @@ dict_stack_clear(int len)
}
/*
+ * Get a pointer to useful "pt_outer" of "pt".
+ */
+ static outer_T *
+get_pt_outer(partial_T *pt)
+{
+ partial_T *ptref = pt->pt_outer_partial;
+
+ if (ptref == NULL)
+ return &pt->pt_outer;
+
+ // partial using partial (recursively)
+ while (ptref->pt_outer_partial != NULL)
+ ptref = ptref->pt_outer_partial;
+ return &ptref->pt_outer;
+}
+
+/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
* called function.
@@ -421,13 +438,13 @@ call_dfunc(
return FAIL;
if (pt != NULL)
{
- ref->or_outer = &pt->pt_outer;
+ ref->or_outer = get_pt_outer(pt);
++pt->pt_refcount;
ref->or_partial = pt;
}
else if (ufunc->uf_partial != NULL)
{
- ref->or_outer = &ufunc->uf_partial->pt_outer;
+ ref->or_outer = get_pt_outer(ufunc->uf_partial);
++ufunc->uf_partial->pt_refcount;
ref->or_partial = ufunc->uf_partial;
}
@@ -5086,7 +5103,9 @@ call_def_function(
goto failed_early;
if (partial != NULL)
{
- if (partial->pt_outer.out_stack == NULL)
+ outer_T *outer = get_pt_outer(partial);
+
+ if (outer->out_stack == NULL)
{
if (current_ectx != NULL)
{
@@ -5099,7 +5118,7 @@ call_def_function(
}
else
{
- ectx.ec_outer_ref->or_outer = &partial->pt_outer;
+ ectx.ec_outer_ref->or_outer = outer;
++partial->pt_refcount;
ectx.ec_outer_ref->or_partial = partial;
}