patch 9.2.0902: Vim9: iterating over a tuple leaks memory
Commit:
https://github.com/vim/vim/commit/6d2f94baa38751fe1d42c2654c049b740a96b335
Author: Samuel Schlesinger <
sgschl...@gmail.com>
Date: Mon Aug 3 19:54:50 2026 +0000
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 <
sgschl...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/eval.c b/src/eval.c
index 7d212b5b3..d5373c099 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 68bdca08c..ace890c04 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 dcea5c2ab..851013e9e 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,
/**/