patch 9.2.0049: Vim9: typename() wrong for lists/dicts/tuples with shared references
Commit: https://github.com/vim/vim/commit/b1d4b03058ca7f7c39959dcc8f9efae28ddad754 Author: Hirohito Higashi <[email protected]> Date: Tue Feb 24 21:22:38 2026 +0000 patch 9.2.0049: Vim9: typename() wrong for lists/dicts/tuples with shared references Problem: Vim9: typename() returns wrong type for lists/dicts/tuples with shared references (Mao-Yining). Solution: Reset CopyID after processing the item so it can be re-inspected if encountered again via a different reference (Hirohito Higashi). fixes: #19490 closes: #19492 Signed-off-by: Hirohito Higashi <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim index 191546a5e..26609a20f 100644 --- a/src/testdir/test_tuple.vim +++ b/src/testdir/test_tuple.vim @@ -2238,6 +2238,14 @@ func Test_tuple_typename() END call v9.CheckSourceDefAndScriptSuccess(lines) + " Shared (non-circular) references must not be treated as circular. + " repeat() makes all elements point to the same inner tuple object. + let lines =<< trim END + call assert_equal('tuple<tuple<number, number>, tuple<number, number>, tuple<number, number>>', ((1, 2),)->repeat(3)->typename()) + call assert_equal('list<tuple<number, number>>', [(1, 2)]->repeat(3)->typename()) + END + call v9.CheckSourceLegacyAndVim9Success(lines) + " When a tuple item is used in a "for" loop, the type is tuple<any> let lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index d5521cf87..d3effd834 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -4983,6 +4983,22 @@ def Test_typename() endif var l: list<func(list<number>): any> = [function('min')] assert_equal('list<func(list<number>): any>', typename(l)) + + # Check that circular list/dict references don't cause infinite recursion. + # Use legacy script where lv_type is not set so the copyID mechanism is used. + v9.CheckSourceLegacySuccess([ + 'let circ_l = []', + 'call add(circ_l, circ_l)', + "call assert_equal('list<list<any>>', typename(circ_l))", + 'let circ_d = {}', + "let circ_d['self'] = circ_d", + "call assert_equal('dict<dict<any>>', typename(circ_d))", + ]) + + # Shared (non-circular) references must not be treated as circular. + # repeat() makes all elements point to the same inner list/dict object. + assert_equal('list<list<string>>', [[" "]]->repeat(3)->typename()) + assert_equal('list<dict<number>>', [{'a': 1}]->repeat(3)->typename()) enddef def Test_undofile() diff --git a/src/version.c b/src/version.c index 478b9f1bc..620e1a5bd 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 49, /**/ 48, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index f6bf1b255..be578f26b 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -612,6 +612,10 @@ list_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags) common_type(typval2type(&li->li_tv, copyID, type_gap, TVTT_DO_MEMBER), member_type, &member_type, type_gap); + // Reset copyID so that a shared reference to this list (not a circular + // reference) can be processed again to get the correct type. + l->lv_copyID = 0; + return get_list_type(member_type, type_gap); } @@ -661,6 +665,9 @@ tuple_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags) if (ga_grow(&tuple_types_ga, 1) == FAIL) { ga_clear(&tuple_types_ga); + // Reset copyID so that a shared reference to this tuple can be + // processed again. + tuple->tv_copyID = 0; return NULL; } ((type_T **)tuple_types_ga.ga_data)[tuple_types_ga.ga_len] = type; @@ -670,6 +677,10 @@ tuple_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags) type_T *tuple_type = get_tuple_type(&tuple_types_ga, type_gap); ga_clear(&tuple_types_ga); + // Reset copyID so that a shared reference to this tuple (not a circular + // reference) can be processed again to get the correct type. + tuple->tv_copyID = 0; + return tuple_type; } @@ -716,6 +727,10 @@ dict_typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags) common_type(typval2type(value, copyID, type_gap, TVTT_DO_MEMBER), member_type, &member_type, type_gap); + // Reset copyID so that a shared reference to this dict (not a circular + // reference) can be processed again to get the correct type. + d->dv_copyID = 0; + return get_dict_type(member_type, type_gap); } -- -- 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/E1vuzyo-00BOCl-GU%40256bit.org.
