Commit: patch 9.2.0902: Vim9: iterating over a tuple leaks memory

3 views
Skip to first unread message

Christian Brabandt

unread,
Aug 3, 2026, 4:00:18 PM (19 hours ago) Aug 3
to vim...@googlegroups.com
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,
/**/
Reply all
Reply to author
Forward
0 new messages