# HG changeset patch # User Bram Moolenaar # Date 1648035004 -3600 # Node ID b0c885507de4786f2a5548237e05000c2a1cf048 # Parent f5cd730da5b6ac2436e41b55de815eabbba79491 patch 8.2.4612: Vim9: cannot use a recursive call in a nested function Commit: https://github.com/vim/vim/commit/a915fa010330ee7212e06d3511acd363d04d2d28 Author: Bram Moolenaar Date: Wed Mar 23 11:29:15 2022 +0000 patch 8.2.4612: Vim9: cannot use a recursive call in a nested function Problem: Vim9: cannot use a recursive call in a nested function. (Sergey Vlasov) Solution: Define the funcref before compiling the function. (closes #9989) diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -38,7 +38,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isn 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 --- 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 --- 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 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -818,6 +818,7 @@ compile_nested_function(exarg_T *eap, cc 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, cc } } + // 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, cc 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 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -1040,7 +1040,7 @@ compile_lambda(char_u **arg, cctx_T *cct // 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 --- 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 * 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