# HG changeset patch # User Bram Moolenaar # Date 1601143203 -7200 # Node ID df1d7a560b35c8fd71cf21da7fa4101773bc1b74 # Parent 6fc35e257910e5f8b45659836898062f6f8b9e80 patch 8.2.1749: Vim9: crash when closure fails in nested function Commit: https://github.com/vim/vim/commit/c70bdab0b8a8262a3784084aa1e6271fee8452f1 Author: Bram Moolenaar Date: Sat Sep 26 19:59:38 2020 +0200 patch 8.2.1749: Vim9: crash when closure fails in nested function Problem: Vim9: crash when closure fails in nested function. Solution: Handle function returns before dereferencing remaining closures. (closes #7008) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1370,6 +1370,20 @@ def Test_double_closure_fails() CheckScriptSuccess(lines) enddef +def Test_nested_closure_fails() + let lines =<< trim END + vim9script + def FuncA() + FuncB(0) + enddef + def FuncB(n: number): list + return map([0], {_, v -> n}) + enddef + FuncA() + END + CheckScriptFailure(lines, 'E1012:') +enddef + def Test_sort_return_type() let res: list res = [1, 2, 3]->sort() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1749, +/**/ 1748, /**/ 1747, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -310,9 +310,12 @@ handle_closure_in_use(ectx_T *ectx, int // Check if any created closure is still in use. for (idx = 0; idx < closure_count; ++idx) { - partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len - - closure_count + idx]; - + partial_T *pt; + int off = gap->ga_len - closure_count + idx; + + if (off < 0) + continue; // count is off or already done + pt = ((partial_T **)gap->ga_data)[off]; if (pt->pt_refcount > 1) { int refcount = pt->pt_refcount; @@ -2734,14 +2737,14 @@ done: ret = OK; failed: - // Also deal with closures when failed, they may already be in use - // somewhere. - handle_closure_in_use(&ectx, FALSE); - // When failed need to unwind the call stack. while (ectx.ec_frame_idx != initial_frame_idx) func_return(&ectx); + // Deal with any remaining closures, they may be in use somewhere. + if (ectx.ec_funcrefs.ga_len > 0) + handle_closure_in_use(&ectx, FALSE); + estack_pop(); current_sctx = save_current_sctx;