From: Hirohito Higashi Date: Fri, 27 Feb 2026 19:03:18 +0000 (+0000) Subject: patch 9.2.0071: Vim9: lambda function deleted on re-sourcing X-Git-Tag: v9.2.0071^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c598c4de275e9075583bc7fe628bb2a50868acfd;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 83499999da..759226a417 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, '.*\(\d\+\).*', '\1', '') + instr = execute('disassemble ' .. name) + assert_match('\d UCALL <80>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 ed946d85db..022bdeb12b 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 0fde64014f..4524be1da5 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 c11e913b0a..90156bfa08 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 */) {