]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.4612: Vim9: cannot use a recursive call in a nested function v8.2.4612
authorBram Moolenaar <Bram@vim.org>
Wed, 23 Mar 2022 11:29:15 +0000 (11:29 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 23 Mar 2022 11:29:15 +0000 (11:29 +0000)
Problem:    Vim9: cannot use a recursive call in a nested function. (Sergey
            Vlasov)
Solution:   Define the funcref before compiling the function. (closes #9989)

src/proto/vim9instr.pro
src/testdir/test_vim9_func.vim
src/version.c
src/vim9compile.c
src/vim9expr.c
src/vim9instr.c

index 8da559745a368f0976ea0cd8339bf6763dcbae75..76f3b2102954ea5bb65b17df0575a51d768dde91 100644 (file)
@@ -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);
index e884f0e3c4ceb5857566936884339bbd94a1ed18..06168e477e13333858f9660f676941066c77e80d 100644 (file)
@@ -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()
index 48eb0c5b0143778b9f9b27cd0c438436027595da..f304ef3e6f2ecfab10d86bfb91ebbf2efab7b105 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4612,
 /**/
     4611,
 /**/
index fde818882e6101466afba51e27a1e82ae49007e0..1eb6ce22fc3173c984a48769e5bb2d7156af4e8a 100644 (file)
@@ -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);
index 3a329ccf434c929dab1f973d3dcf2c8a93e769f8..8c7d0b0e9538357e2ad40524210fcb765825b046 100644 (file)
@@ -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);
index 7f23884f3cf9dfdbea1c448e285c107780baf1a3..f0206211d80cbf5604f00e23563f85fb3f845627 100644 (file)
@@ -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