patch 9.2.0071: Vim9: lambda function deleted on re-sourcing

Commit: 
https://github.com/vim/vim/commit/c598c4de275e9075583bc7fe628bb2a50868acfd
Author: Hirohito Higashi <[email protected]>
Date:   Fri Feb 27 19:03:18 2026 +0000

    patch 9.2.0071: Vim9: lambda function deleted on re-sourcing
    
    Problem:  Vim9: lambda function deleted on re-sourcing
              (Mao-Yining)
    Solution: Use ISN_UCALL for script-local def calls inside a lambda
              (Hirohito Higashi).
    
    fixes:  #19509
    closes: #19519
    
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/testdir/test_vim9_disassemble.vim 
b/src/testdir/test_vim9_disassemble.vim
index 83499999d..759226a41 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -1473,6 +1473,31 @@ def Test_disassemble_lambda()
         instr)
 enddef
 
+def s:ScriptLocalDefForLambda()
+enddef
+
+def GlobalDefForLambda()
+enddef
+
+def s:OuterWithLambdaCalls()
+  timer_start(0, (_) => {
+    ScriptLocalDefForLambda()
+    g:GlobalDefForLambda()
+  })
+enddef
+
+def Test_disassemble_lambda_call_types()
+  # Verify that inside a lambda:
+  # - script-local def function call → ISN_UCALL (safe after re-sourcing)
+  # - global def function call → ISN_DCALL (optimal, not deleted on 
re-source)
+  OuterWithLambdaCalls()
+  var instr = execute('disassemble OuterWithLambdaCalls')
+  var name = substitute(instr, '.*\(<lambda>\d\+\).*', ' ', '')
+  instr = execute('disassemble ' .. name)
+  assert_match('\d UCALL <80><fd>R\d\+_ScriptLocalDefForLambda(argc 0)', instr)
+  assert_match('\d DCALL GlobalDefForLambda(argc 0)', instr)
+enddef
+
 def s:LambdaWithType(): number
   var Ref = (a: number) => a + 10
   return Ref(g:value)
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index ed946d85d..022bdeb12 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -1847,6 +1847,41 @@ def Test_vim9script_reload_delfunc()
   g:DoCheck(false)
 enddef
 
+def Test_vim9script_reload_lambda_def_func()
+  CheckFeature timers
+
+  var lines =<< trim END
+    vim9script
+
+    def F()
+      g:call_result += 1
+    enddef
+
+    augroup Xtest933
+      au!
+      au CmdlineLeave : timer_start(0, (_) => F())
+    augroup END
+  END
+
+  g:call_result = 0
+  writefile(lines, 'Xtest933.vim', 'D')
+  source Xtest933.vim
+
+  # Simulate the CmdlineLeave event that fires before the second :so
+  doautocmd CmdlineLeave :
+
+  # Re-source: F is redefined; without the fix this causes E933 when timer 
fires
+  source Xtest933.vim
+
+  # Allow the 0ms timer to fire
+  sleep 10m
+
+  assert_equal(1, g:call_result)
+
+  augroup Xtest933 | au! | augroup END
+  unlet! g:call_result
+enddef
+
 def Test_vim9script_reload_delvar()
   # write the script with a script-local variable
   var lines =<< trim END
diff --git a/src/version.c b/src/version.c
index 0fde64014..4524be1da 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    71,
 /**/
     70,
 /**/
diff --git a/src/vim9instr.c b/src/vim9instr.c
index c11e913b0..90156bfa0 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1909,6 +1909,23 @@ generate_BLOBAPPEND(cctx_T *cctx)
     return OK;
 }
 
+/*
+ * get the instruction type for a function call: ISN_METHODCALL, ISN_DCALL, or
+ * ISN_UCALL.
+ */
+    static isntype_T
+isn_get_calltype(
+       cctx_T      *cctx,
+       ufunc_T     *ufunc,
+       class_T     *cl)
+{
+    return cl != NULL ? ISN_METHODCALL
+       : (ufunc->uf_def_status != UF_NOT_COMPILED
+               && ((cctx->ctx_ufunc->uf_flags & FC_LAMBDA) == 0
+                   || ufunc->uf_name[0] != K_SPECIAL))
+       ? ISN_DCALL : ISN_UCALL;
+}
+
 /*
  * Generate an ISN_DCALL, ISN_UCALL or ISN_METHODCALL instruction.
  * When calling a method on an object, of which we know the interface only,
@@ -1996,9 +2013,7 @@ generate_CALL(
        return FAIL;
     }
 
-    if ((isn = generate_instr(cctx, cl != NULL ? ISN_METHODCALL
-                         : ufunc->uf_def_status != UF_NOT_COMPILED
-                                            ? ISN_DCALL : ISN_UCALL)) == NULL)
+    if ((isn = generate_instr(cctx, isn_get_calltype(cctx, ufunc, cl))) == 
NULL)
        return FAIL;
     if (cl != NULL /* isn->isn_type == ISN_METHODCALL */)
     {

-- 
-- 
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 visit 
https://groups.google.com/d/msgid/vim_dev/E1vw3Il-00G6gO-1l%40256bit.org.

Raspunde prin e-mail lui