Patch 8.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)
Files:      src/vim9compile.c, src/vim9instr.c, src/proto/vim9instr.pro,
            src/vim9expr.c, src/testdir/test_vim9_func.vim


*** ../vim-8.2.4611/src/vim9compile.c   2022-03-20 21:14:08.438143810 +0000
--- src/vim9compile.c   2022-03-23 11:22:19.229157761 +0000
***************
*** 818,823 ****
--- 818,824 ----
      ufunc_T   *ufunc;
      int               r = FAIL;
      compiletype_T   compile_type;
+     isn_T     *funcref_isn = NULL;
  
      if (eap->forceit)
      {
***************
*** 913,918 ****
--- 914,940 ----
        }
      }
  
+     // 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,957 ****
        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);
!     }
  
  theend:
      vim_free(lambda_name);
--- 956,964 ----
        compile_def_function(ufunc, FALSE, CT_NONE, cctx);
  #endif
  
!     // 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);
*** ../vim-8.2.4611/src/vim9instr.c     2022-03-20 21:14:08.438143810 +0000
--- src/vim9instr.c     2022-03-23 11:22:07.529167846 +0000
***************
*** 1172,1180 ****
  
  /*
   * Generate an ISN_FUNCREF instruction.
   */
      int
! generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
  {
      isn_T     *isn;
      type_T    *type;
--- 1172,1181 ----
  
  /*
   * 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, isn_T **isnp)
  {
      isn_T     *isn;
      type_T    *type;
***************
*** 1182,1187 ****
--- 1183,1190 ----
      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
*** ../vim-8.2.4611/src/proto/vim9instr.pro     2022-03-20 21:14:08.438143810 
+0000
--- src/proto/vim9instr.pro     2022-03-23 11:18:40.121317263 +0000
***************
*** 38,44 ****
  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_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);
--- 38,44 ----
  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, 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);
*** ../vim-8.2.4611/src/vim9expr.c      2022-03-16 17:53:58.311925819 +0000
--- src/vim9expr.c      2022-03-23 11:21:23.221204596 +0000
***************
*** 1040,1046 ****
        // 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);
      }
  
      func_ptr_unref(ufunc);
--- 1040,1046 ----
        // 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, NULL);
      }
  
      func_ptr_unref(ufunc);
*** ../vim-8.2.4611/src/testdir/test_vim9_func.vim      2022-03-20 
18:50:56.536291286 +0000
--- src/testdir/test_vim9_func.vim      2022-03-23 11:27:23.732850811 +0000
***************
*** 876,881 ****
--- 876,900 ----
    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()
*** ../vim-8.2.4611/src/version.c       2022-03-22 21:14:51.756456002 +0000
--- src/version.c       2022-03-23 11:25:23.224981155 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4612,
  /**/

-- 
'I generally avoid temptation unless I can't resist it."
                -- Mae West

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220323113005.C30571C0497%40moolenaar.net.

Raspunde prin e-mail lui