summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-03-23 11:29:15 +0000
committerBram Moolenaar <Bram@vim.org>2022-03-23 11:29:15 +0000
commita915fa010330ee7212e06d3511acd363d04d2d28 (patch)
tree632c812369ceff7cfb07ea46358ebfb010868943
parent81b573d7e55bd48988f298ce8e652d902e9bdeba (diff)
patch 8.2.4612: Vim9: cannot use a recursive call in a nested functionv8.2.4612
Problem: Vim9: cannot use a recursive call in a nested function. (Sergey Vlasov) Solution: Define the funcref before compiling the function. (closes #9989)
-rw-r--r--src/proto/vim9instr.pro2
-rw-r--r--src/testdir/test_vim9_func.vim19
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c43
-rw-r--r--src/vim9expr.c2
-rw-r--r--src/vim9instr.c5
6 files changed, 52 insertions, 21 deletions
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 8da559745a..76f3b21029 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -38,7 +38,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid,
int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
int generate_NEWLIST(cctx_T *cctx, int count);
int generate_NEWDICT(cctx_T *cctx, int count);
-int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc);
+int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index e884f0e3c4..06168e477e 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -876,6 +876,25 @@ def Test_nested_function()
END
v9.CheckScriptSuccess(lines)
+ # nested function with recursive call
+ lines =<< trim END
+ vim9script
+
+ def MyFunc(): number
+ def Fib(n: number): number
+ if n < 2
+ return 1
+ endif
+ return Fib(n - 2) + Fib(n - 1)
+ enddef
+
+ return Fib(5)
+ enddef
+
+ assert_equal(8, MyFunc())
+ END
+ v9.CheckScriptSuccess(lines)
+
lines =<< trim END
vim9script
def Outer()
diff --git a/src/version.c b/src/version.c
index 48eb0c5b01..f304ef3e6f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4612,
+/**/
4611,
/**/
4610,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index fde818882e..1eb6ce22fc 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -818,6 +818,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
ufunc_T *ufunc;
int r = FAIL;
compiletype_T compile_type;
+ isn_T *funcref_isn = NULL;
if (eap->forceit)
{
@@ -913,6 +914,27 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
}
}
+ // Define the funcref before compiling, so that it is found by any
+ // recursive call.
+ if (is_global)
+ {
+ r = generate_NEWFUNC(cctx, lambda_name, func_name);
+ func_name = NULL;
+ lambda_name = NULL;
+ }
+ else
+ {
+ // Define a local variable for the function reference.
+ lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start,
+ TRUE, ufunc->uf_func_type);
+
+ if (lvar == NULL)
+ goto theend;
+ if (generate_FUNCREF(cctx, ufunc, &funcref_isn) == FAIL)
+ goto theend;
+ r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
+ }
+
compile_type = get_compile_type(ufunc);
#ifdef FEAT_PROFILE
// If the outer function is profiled, also compile the nested function for
@@ -934,24 +956,9 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
compile_def_function(ufunc, FALSE, CT_NONE, cctx);
#endif
- if (is_global)
- {
- r = generate_NEWFUNC(cctx, lambda_name, func_name);
- func_name = NULL;
- lambda_name = NULL;
- }
- else
- {
- // Define a local variable for the function reference.
- lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start,
- TRUE, ufunc->uf_func_type);
-
- if (lvar == NULL)
- goto theend;
- if (generate_FUNCREF(cctx, ufunc) == FAIL)
- goto theend;
- r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
- }
+ // If a FUNCREF instruction was generated, set the index after compiling.
+ if (funcref_isn != NULL && ufunc->uf_def_status == UF_COMPILED)
+ funcref_isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
theend:
vim_free(lambda_name);
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 3a329ccf43..8c7d0b0e95 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -1040,7 +1040,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
// The function reference count will be 1. When the ISN_FUNCREF
// instruction is deleted the reference count is decremented and the
// function is freed.
- return generate_FUNCREF(cctx, ufunc);
+ return generate_FUNCREF(cctx, ufunc, NULL);
}
func_ptr_unref(ufunc);
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 7f23884f3c..f0206211d8 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1172,9 +1172,10 @@ generate_NEWDICT(cctx_T *cctx, int count)
/*
* Generate an ISN_FUNCREF instruction.
+ * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
*/
int
-generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
+generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
{
isn_T *isn;
type_T *type;
@@ -1182,6 +1183,8 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL;
+ if (isnp != NULL)
+ *isnp = isn;
if (ufunc->uf_def_status == UF_NOT_COMPILED)
isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
else