# HG changeset patch # User Bram Moolenaar # Date 1596987003 -7200 # Node ID 571832713efa4381d7390f0756b5db03a05301db # Parent e5d6e27da4435029acc1483a8c686f172e58fa42 patch 8.2.1407: Vim9: type of list and dict only depends on first item Commit: https://github.com/vim/vim/commit/127542bcebeb6480493b09d75a3be1d98a5f7797 Author: Bram Moolenaar Date: Sun Aug 9 17:22:04 2020 +0200 patch 8.2.1407: Vim9: type of list and dict only depends on first item Problem: Vim9: type of list and dict only depends on first item. Solution: Use all items to decide about the type. diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -619,6 +619,8 @@ called in the same way the declaration i Custom types can be defined with `:type`: > :type MyList list +Custom types must start with a capital letter, to avoid name clashes with +builtin types added later, similarly to user functions. {not implemented yet} And classes and interfaces can be used as types: > @@ -645,6 +647,12 @@ declaring a variable and giving it a val let var = 0 " infers number type let var = 'hello' " infers string type +The type of a list and dictionary comes from the common type of the values. +If the values all have the same type, that type is used for the list or +dictionary. If there is a mix of types, the "any" type is used. > + [1, 2, 3] list + ['a', 'b', 'c'] list + [1, 'x', 3] list ============================================================================== diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -14,6 +14,7 @@ int check_type(type_T *expected, type_T char_u *skip_type(char_u *start, int optional); type_T *parse_type(char_u **arg, garray_T *type_gap); void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap); +type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap); char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1334,7 +1334,17 @@ def Test_expr7_list() # list assert_equal(g:list_empty, []) assert_equal(g:list_empty, [ ]) - assert_equal(g:list_mixed, [1, 'b', false,]) + + let numbers: list = [1, 2, 3] + numbers = [1] + numbers = [] + + let strings: list = ['a', 'b', 'c'] + strings = ['x'] + strings = [] + + let mixed: list = [1, 'b', false,] + assert_equal(g:list_mixed, mixed) assert_equal('b', g:list_mixed[1]) echo [1, @@ -1348,6 +1358,10 @@ def Test_expr7_list() call CheckDefFailure(["let x = g:list_mixed["], 'E1097:') call CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:') call CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:') + call CheckDefFailure(["let l: list = [234, 'x']"], 'E1013:') + call CheckDefFailure(["let l: list = ['x', 234]"], 'E1013:') + call CheckDefFailure(["let l: list = [234, 'x']"], 'E1013:') + call CheckDefFailure(["let l: list = ['x', 123]"], 'E1013:') enddef def Test_expr7_list_vim9script() @@ -1437,6 +1451,19 @@ def Test_expr7_dict() let val = 1 assert_equal(g:dict_one, {key: val}) + let numbers: dict = #{a: 1, b: 2, c: 3} + numbers = #{a: 1} + numbers = #{} + + let strings: dict = #{a: 'a', b: 'b', c: 'c'} + strings = #{a: 'x'} + strings = #{} + + let mixed: dict = #{a: 'a', b: 42} + mixed = #{a: 'x'} + mixed = #{a: 234} + mixed = #{} + call CheckDefFailure(["let x = #{8: 8}"], 'E1014:') call CheckDefFailure(["let x = #{xxx}"], 'E720:') call CheckDefFailure(["let x = #{xxx: 1", "let y = 2"], 'E722:') @@ -1449,6 +1476,11 @@ def Test_expr7_dict() call CheckDefFailure(["let x = x + 1"], 'E1001:') call CheckDefExecFailure(["let x = g:anint.member"], 'E715:') call CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:') + + call CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1013:') + call CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1013:') + call CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1013:') + call CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1013:') enddef def Test_expr7_dict_vim9script() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1407, +/**/ 1406, /**/ 1405, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1110,17 +1110,15 @@ generate_NEWLIST(cctx_T *cctx, int count return FAIL; isn->isn_arg.number = count; + // get the member type from all the items on the stack. + member = get_member_type_from_stack( + ((type_T **)stack->ga_data) + stack->ga_len, count, 1, + cctx->ctx_type_list); + type = get_list_type(member, cctx->ctx_type_list); + // drop the value types stack->ga_len -= count; - // Use the first value type for the list member type. Use "any" for an - // empty list. - if (count > 0) - member = ((type_T **)stack->ga_data)[stack->ga_len]; - else - member = &t_void; - type = get_list_type(member, cctx->ctx_type_list); - // add the list type to the type stack if (ga_grow(stack, 1) == FAIL) return FAIL; @@ -1146,17 +1144,14 @@ generate_NEWDICT(cctx_T *cctx, int count return FAIL; isn->isn_arg.number = count; + member = get_member_type_from_stack( + ((type_T **)stack->ga_data) + stack->ga_len, count, 2, + cctx->ctx_type_list); + type = get_dict_type(member, cctx->ctx_type_list); + // drop the key and value types stack->ga_len -= 2 * count; - // Use the first value type for the list member type. Use "void" for an - // empty dict. - if (count > 0) - member = ((type_T **)stack->ga_data)[stack->ga_len + 1]; - else - member = &t_void; - type = get_dict_type(member, cctx->ctx_type_list); - // add the dict type to the type stack if (ga_grow(stack, 1) == FAIL) return FAIL; diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -789,6 +789,42 @@ common_type(type_T *type1, type_T *type2 *dest = &t_any; } +/* + * Get the member type of a dict or list from the items on the stack. + * "stack_top" points just after the last type on the type stack. + * For a list "skip" is 1, for a dict "skip" is 2, keys are skipped. + * Returns &t_void for an empty list or dict. + * Otherwise finds the common type of all items. + */ + type_T * +get_member_type_from_stack( + type_T **stack_top, + int count, + int skip, + garray_T *type_gap) +{ + int i; + type_T *result; + type_T *type; + + // Use "any" for an empty list or dict. + if (count == 0) + return &t_void; + + // Use the first value type for the list member type, then find the common + // type from following items. + result = *(stack_top -(count * skip) + skip - 1); + for (i = 1; i < count; ++i) + { + if (result == &t_any) + break; // won't get more common + type = *(stack_top -((count - i) * skip) + skip - 1); + common_type(type, result, &result, type_gap); + } + + return result; +} + char * vartype_name(vartype_T type) {