]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0071: Vim9: lambda function deleted on re-sourcing v9.2.0071
authorHirohito Higashi <h.east.727@gmail.com>
Fri, 27 Feb 2026 19:03:18 +0000 (19:03 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 27 Feb 2026 19:03:18 +0000 (19:03 +0000)
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 <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9instr.c

index 83499999da5539ba1c7dcea66ad60dbd237dc549..759226a417439e55bd080ffbb6d975747243b790 100644 (file)
@@ -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\+\).*', '\1', '')
+  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)
index ed946d85db638fd05c7ac3179bc75c10bd8d9d0e..022bdeb12bf368842b8eed837695435da41b3ead 100644 (file)
@@ -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
index 0fde64014fe661dd64915054a19d0d4686ce1df9..4524be1da5adb3d9e9b091e5c1a8fa7bcc7cd472 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    71,
 /**/
     70,
 /**/
index c11e913b0a64692d137780a81c66c1461f3b96fe..90156bfa081b7fb65129798dc3abf20650002bd6 100644 (file)
@@ -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 */)
     {