From: Samuel Schlesinger Date: Mon, 3 Aug 2026 19:54:50 +0000 (+0000) Subject: patch 9.2.0902: Vim9: iterating over a tuple leaks memory X-Git-Tag: v9.2.0902^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6d2f94baa38751fe1d42c2654c049b740a96b335;p=thirdparty%2Fvim.git patch 9.2.0902: Vim9: iterating over a tuple leaks memory Problem: Looping over a tuple with ":for" copies each item with copy_tv() but never clears the copy, leaking the value on every iteration (26MB over 100k iterations of a two-string tuple). Container items keep an extra reference forever, also defeating garbage collection. Solution: Clear the copied typval on both return paths, like the string branch of next_for_item() already does (Samuel Schlesinger). closes: #20914 Supported by AI. Signed-off-by: Samuel Schlesinger Signed-off-by: Christian Brabandt --- diff --git a/src/eval.c b/src/eval.c index 7d212b5b30..d5373c0991 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3005,9 +3005,12 @@ next_for_item(void *fi_void, char_u *arg) ++fi->fi_tuple_idx; ++fi->fi_bi; if (skip_assign) - return TRUE; - return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon, + result = TRUE; + else + result = ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon, fi->fi_varcount, flag, NULL) == OK; + clear_tv(&tv); + return result; } item = fi->fi_lw.lw_item; diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim index 68bdca08c2..ace890c040 100644 --- a/src/testdir/test_tuple.vim +++ b/src/testdir/test_tuple.vim @@ -778,6 +778,20 @@ func Test_tuple_for() LET sum += v2 endfor call assert_equal(0, sum) + + #" iterating over string items; the copied item must not be leaked + VAR res = '' + for v3 in ('a', 'bb', 'ccc') + LET res ..= v3 + endfor + call assert_equal('abbccc', res) + + #" iterating over container items must not leak a reference + VAR flat = [] + for v4 in (['a'], ['b', 'c']) + LET flat += v4 + endfor + call assert_equal(['a', 'b', 'c'], flat) END call v9.CheckSourceLegacyAndVim9Success(lines) @@ -792,6 +806,18 @@ func Test_tuple_for() END call v9.CheckSourceSuccess(lines) + " ignoring the for loop assignment using '_'; string items must not be + " leaked + let lines =<< trim END + vim9script + var count = 0 + for _ in ('a', 'bb', 'ccc') + count += 1 + endfor + assert_equal(3, count) + END + call v9.CheckSourceSuccess(lines) + let lines =<< trim END var sum = 0 for v in null_tuple diff --git a/src/version.c b/src/version.c index dcea5c2ab2..851013e9e5 100644 --- a/src/version.c +++ b/src/version.c @@ -763,6 +763,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 902, /**/ 901, /**/