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 <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi
2026-02-27 19:03:18 +00:00
committed by Christian Brabandt
parent 2fa34b6422
commit c598c4de27
4 changed files with 80 additions and 3 deletions
+25
View 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 callISN_UCALL (safe after re-sourcing)
# - global def function callISN_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)
+35
View 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
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
71,
/**/
70,
/**/
+18 -3
View 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 */)
{