fix: typename() returns wrong type for lists/dicts/tuples with shared references#19492
fix: typename() returns wrong type for lists/dicts/tuples with shared references#19492h-east wants to merge 1 commit intovim:masterfrom
Conversation
|
The document didn’t describe that how repeat() deal with a list within a list. Would it be more sensible to use copy() or deepcopy(), or just add a note to the documentation? |
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where typename() incorrectly returned list<any>, dict<any>, or tuple<any> for containers with shared (non-circular) references to inner objects. The issue occurred when functions like repeat() created multiple elements pointing to the same inner object, causing the copyID mechanism (used to detect circular references) to incorrectly treat shared references as circular.
Changes:
- Added copyID reset logic in
list_typval2type(),tuple_typval2type(), anddict_typval2type()functions - Added comprehensive tests for circular references and shared references for lists, dicts, and tuples
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/vim9type.c | Resets copyID to 0 after processing container members in three type inference functions to distinguish shared references from circular references |
| src/testdir/test_vim9_builtin.vim | Adds tests for circular references and shared references in lists and dicts using typename() |
| src/testdir/test_tuple.vim | Adds tests for shared references in tuples using typename() |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
thanks |
Fixes: #19490
Problem
typename()incorrectly returned list for the member type when a containerheld multiple references to the same inner object. For example:
repeat()makes all elements point to the same inner list/dict/tuple object(shallow copy via copy_tv()). During type inference, list_typval2type(),
dict_typval2type(), and tuple_typval2type() use a copyID to guard against
infinite recursion on circular references. However, they never cleared the
copyID after finishing, so when the same object was encountered again as a
sibling element (a shared reference, not a circular one), it was mistakenly
treated as a circular reference and t_list_any / t_dict_any / t_tuple_any was
returned.
Solution
Reset lv_copyID / dv_copyID / tv_copyID to 0 after all members have been
processed in each of the three functions. The circular reference guard still
works correctly because the copyID is set before processing members and only
cleared after — so any re-entrant call on the same object during its own
traversal is still detected and short-circuits with t_*_any.