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

Christian Brabandt <[email protected]> Mon, 3 Aug 2026 22:00:09 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0902: Vim9: iterating over a tuple leaks memory

Commit: https://github.com/vim/vim/commit/6d2f94baa38751fe1d42c2654c049b740a96b335
Author: Samuel Schlesinger <[email protected]>
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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

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,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wqypV-00BNY2-51%40256bit.org.