Commit: patch 9.2.0958: Vim9: wrong type for the rest of a tuple in an unpack assignment
Christian Brabandt <[email protected]>
| Newsgroups | gmane.editors.vim.devel |
|---|---|
| Message-ID | <[email protected]> |
patch 9.2.0958: Vim9: wrong type for the rest of a tuple in an unpack assignment Commit: https://github.com/vim/vim/commit/d4703e569d655c2db1642425ec649edeeaa57751 Author: Hirohito Higashi <[email protected]> Date: Sun Aug 16 15:42:56 2026 +0000 patch 9.2.0958: Vim9: wrong type for the rest of a tuple in an unpack assignment Problem: In an unpack assignment the variable after the ";" gets the type of the whole tuple instead of the type of the remaining items. When the assigned value has type "any" it even gets a list type, so that a tuple cannot be assigned to it later (Mao-Yining). Solution: Give the variable the type of the remaining items. Keep "any" when it is not known whether a list or a tuple is assigned (Hirohito Higashi). fixes: #21057 fixes: #21058 closes: #21059 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Hirohito Higashi <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index be987bf02..e69b62acd 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.2. Last change: 2026 Jul 29 +*vim9.txt* For Vim version 9.2. Last change: 2026 Aug 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -583,6 +583,15 @@ possible. Each variable can have a type or infer it from the value: > Use this only when there is a list with values, declaring one variable per line is much easier to read and change later. +The type of a variable that does not have a declared type is the type of the +value it gets. The variable after the ";" gets the remaining items: for a +list this is a list of the same member type, for a tuple this is a tuple of +the types of the remaining items: > + var [v1; v2] = [1, 2, 3] # v2 has type list<number> + var [v3; v4] = (1, 'a', true) # v4 has type tuple<string, bool> +When the value has type "any" the item types are not known and every variable +gets type "any". + Constants ~ *vim9-const* *vim9-final* diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim index ace890c04..00428a5b0 100644 --- a/src/testdir/test_tuple.vim +++ b/src/testdir/test_tuple.vim @@ -632,6 +632,47 @@ func Test_multi_assign_from_tuple() END call v9.CheckSourceSuccess(lines) + " The rest gets the type of the remaining items, not of the whole tuple + let lines =<< trim END + vim9script + def Fn() + var t: tuple<dict<any>, list<any>> = ({}, []) + var [a; b] = t + assert_equal('tuple<list<any>>', typename(b)) + b = ([],) + enddef + Fn() + END + call v9.CheckSourceSuccess(lines) + + " The rest of a tuple literal also gets the remaining item types + let lines =<< trim END + vim9script + def Fn() + var [a; b] = (true, false, true) + assert_equal((false, true), b) + assert_equal('tuple<bool, bool>', typename(b)) + enddef + Fn() + END + call v9.CheckSourceSuccess(lines) + + " The rest of a value with type "any" can be a tuple + let lines =<< trim END + vim9script + var d: dict<tuple<dict<any>, list<any>>> + def Fn(): tuple<dict<any>, list<any>> + return ({}, []) + enddef + def Test() + var [a; b] = d->get('a', (-1, {}, [])) + b = Fn() + assert_equal('tuple<dict<any>, list<any>>', typename(b)) + enddef + Test() + END + call v9.CheckSourceSuccess(lines) + let lines =<< trim END VAR [v1, v2] = ('a', 'b', 'c') END diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 759226a41..88e794ec7 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -656,6 +656,7 @@ def Test_disassemble_list_assign() '\d\+ CHECKTYPE string stack\[-1\] var 2\_s*' .. '\d\+ STORE $1\_s*' .. '\d\+ SLICE 2\_s*' .. + '\d\+ CHECKTYPE list<any> stack\[-1\] var 3\_s*' .. '\d\+ STORE $2\_s*' .. '\d\+ RETURN void', res) diff --git a/src/version.c b/src/version.c index bfe225943..9be27f5a3 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 */ +/**/ + 958, /**/ 957, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 65d7580f9..2aeef4095 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3060,6 +3060,11 @@ compile_assign_list_check_rhs_type(cctx_T *cctx, cac_T *cac) TYPECHK_TUPLE_OK, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; + // The check accepts both a list and a tuple. Keep "any", making it a list + // would reject a tuple later on. + if (stacktype->tt_type == VAR_ANY) + set_type_on_stack(cctx, &t_any, 0); + if (stacktype->tt_type == VAR_TUPLE) { if (stacktype->tt_argcount != 1) diff --git a/src/vim9instr.c b/src/vim9instr.c index 5e9ab38b7..069818b35 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1089,6 +1089,43 @@ generate_GETITEM(cctx_T *cctx, int index, int with_op) return push_type_stack(cctx, item_type); } +/* + * Set the type of the tuple that is left after dropping the first "count" + * items of the tuple with type "type". + */ + static void +set_tuple_slice_type_on_stack(type_T *type, int count, cctx_T *cctx) +{ + garray_T tuple_types_ga; + + // The item types are only known for a tuple with a fixed number of items. + if ((type->tt_flags & TTFLAG_VARARGS) || type->tt_argcount <= count) + { + set_type_on_stack(cctx, &t_tuple_any, 0); + return; + } + + ga_init2(&tuple_types_ga, sizeof(type_T *), 10); + + for (int i = count; i < type->tt_argcount; i++) + { + if (ga_grow(&tuple_types_ga, 1) == FAIL) + { + ga_clear(&tuple_types_ga); + set_type_on_stack(cctx, &t_tuple_any, 0); + return; + } + ((type_T **)tuple_types_ga.ga_data)[tuple_types_ga.ga_len] = + type->tt_args[i]; + tuple_types_ga.ga_len++; + } + + set_type_on_stack(cctx, + get_tuple_type(&tuple_types_ga, cctx->ctx_type_list), 0); + + ga_clear(&tuple_types_ga); +} + /* * Generate an ISN_SLICE instruction with "count". */ @@ -1096,11 +1133,19 @@ generate_GETITEM(cctx_T *cctx, int index, int with_op) generate_SLICE(cctx_T *cctx, int count) { isn_T *isn; + type_T *type; RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_SLICE)) == NULL) return FAIL; isn->isn_arg.number = count; + + // Slicing a tuple leaves a tuple with the remaining item types, not the + // type of the whole tuple. + type = get_type_on_stack(cctx, 0); + if (type->tt_type == VAR_TUPLE) + set_tuple_slice_type_on_stack(type, count, cctx); + return OK; } diff --git a/src/vim9type.c b/src/vim9type.c index dfa6686a9..49179c722 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -2373,7 +2373,8 @@ get_item_type(type_T *type) return type->tt_args[0]; } - return type->tt_member; + // For "any" the item type is not known. + return type->tt_member == NULL ? &t_any : type->tt_member; } /* -- -- 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/E1wvdHJ-007240-Nn%40256bit.org.