To: vim_dev@googlegroups.com Subject: Patch 8.2.1407 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1407 Problem: Vim9: type of list and dict only depends on first item. Solution: Use all items to decide about the type. Files: src/vim9compile.c, src/vim9type.c, src/proto/vim9type.pro, src/testdir/test_vim9_expr.vim, runtime/doc/vim9.txt *** ../vim-8.2.1406/src/vim9compile.c 2020-08-09 15:24:52.161418771 +0200 --- src/vim9compile.c 2020-08-09 17:05:56.020952497 +0200 *************** *** 1110,1126 **** return FAIL; isn->isn_arg.number = count; // 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; --- 1110,1124 ---- 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; // add the list type to the type stack if (ga_grow(stack, 1) == FAIL) return FAIL; *************** *** 1146,1162 **** return FAIL; isn->isn_arg.number = count; // 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; --- 1144,1157 ---- 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; // add the dict type to the type stack if (ga_grow(stack, 1) == FAIL) return FAIL; *** ../vim-8.2.1406/src/vim9type.c 2020-08-09 15:24:52.165418739 +0200 --- src/vim9type.c 2020-08-09 17:01:58.410029553 +0200 *************** *** 789,794 **** --- 789,830 ---- *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) { *** ../vim-8.2.1406/src/proto/vim9type.pro 2020-08-09 15:24:52.165418739 +0200 --- src/proto/vim9type.pro 2020-08-09 17:01:39.334129483 +0200 *************** *** 14,19 **** --- 14,20 ---- 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 : */ *** ../vim-8.2.1406/src/testdir/test_vim9_expr.vim 2020-08-08 15:45:58.233358630 +0200 --- src/testdir/test_vim9_expr.vim 2020-08-09 17:14:58.227216373 +0200 *************** *** 1334,1340 **** # list assert_equal(g:list_empty, []) assert_equal(g:list_empty, [ ]) ! assert_equal(g:list_mixed, [1, 'b', false,]) assert_equal('b', g:list_mixed[1]) echo [1, --- 1334,1350 ---- # list assert_equal(g:list_empty, []) assert_equal(g:list_empty, [ ]) ! ! 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,1353 **** --- 1358,1367 ---- 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,1442 **** --- 1451,1469 ---- 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,1454 **** --- 1476,1486 ---- 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() *** ../vim-8.2.1406/runtime/doc/vim9.txt 2020-08-01 17:00:00.169379380 +0200 --- runtime/doc/vim9.txt 2020-08-09 17:19:24.870549881 +0200 *************** *** 14,20 **** THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE ! 1 What is Vim9 script? |vim9-script| 2. Differences |vim9-differences| 3. New style functions |fast-functions| 4. Types |vim9-types| --- 14,20 ---- THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE ! 1. What is Vim9 script? |vim9-script| 2. Differences |vim9-differences| 3. New style functions |fast-functions| 4. Types |vim9-types| *************** *** 119,129 **** When using `:function` or `:def` to specify a new function at the script level in a Vim9 script, the function is local to the script, as if "s:" was prefixed. Using the "s:" prefix is optional. To define or use a global ! function or variable the "g:" prefix must be used. For functions in an autoload script the "name#" prefix is sufficient. > def ThisFunction() # script-local def s:ThisFunction() # script-local def g:ThatFunction() # global def scriptname#function() # autoload When using `:function` or `:def` to specify a new function inside a function, --- 119,130 ---- When using `:function` or `:def` to specify a new function at the script level in a Vim9 script, the function is local to the script, as if "s:" was prefixed. Using the "s:" prefix is optional. To define or use a global ! function or variable the "g:" prefix should be used. For functions in an autoload script the "name#" prefix is sufficient. > def ThisFunction() # script-local def s:ThisFunction() # script-local def g:ThatFunction() # global + def ThatFunction() # global if no local ThatFunction() def scriptname#function() # autoload When using `:function` or `:def` to specify a new function inside a function, *************** *** 132,147 **** function, using the "g:" prefix. When referring to a function and no "s:" or "g:" prefix is used, Vim will ! search for the function in this order: ! - Local to the current scope and outer scopes up to the function scope. ! - Local to the current script file. ! - Imported functions, see `:import`. In all cases the function must be defined before used. That is when it is first called or when `:defcompile` causes the call to be compiled. ! The result is that functions and variables without a namespace can always be found in the script, either defined there or imported. Global functions and ! variables could be defined anywhere (good luck finding where!). Global functions can be still be defined and deleted at nearly any time. In Vim9 script script-local functions are defined once when the script is sourced --- 133,146 ---- function, using the "g:" prefix. When referring to a function and no "s:" or "g:" prefix is used, Vim will ! prefer using a local function (in the function scope, script scope or ! imported) before looking for a global function. In all cases the function must be defined before used. That is when it is first called or when `:defcompile` causes the call to be compiled. ! The result is that functions and variables without a namespace can usually be found in the script, either defined there or imported. Global functions and ! variables could be defined anywhere (good luck finding out where!). Global functions can be still be defined and deleted at nearly any time. In Vim9 script script-local functions are defined once when the script is sourced *************** *** 193,199 **** without `:let`, because they are not really declared, they can also be deleted with `:unlet`. ! Variables cannot shadow previously defined variables. Variables may shadow Ex commands, rename the variable if needed. Global variables and user defined functions must be prefixed with "g:", also --- 192,199 ---- without `:let`, because they are not really declared, they can also be deleted with `:unlet`. ! Variables and functions cannot shadow previously defined or imported variables ! and functions. Variables may shadow Ex commands, rename the variable if needed. Global variables and user defined functions must be prefixed with "g:", also *************** *** 232,242 **** 'foobar'->Process() ('foobar')->Process() ! In rare case there is ambiguity between a function name and an Ex command, use ! ":" to make clear you want to use the Ex command. For example, there is both ! the `:substitute` command and the `substitute()` function. When the line ! starts with `substitute(` this will use the function. Prepend a colon to use ! the command instead: > :substitute(pattern (replacement ( Note that while variables need to be defined before they can be used, --- 232,242 ---- 'foobar'->Process() ('foobar')->Process() ! In rare case there is ambiguity between a function name and an Ex command, ! prepend ":" to make clear you want to use the Ex command. For example, there ! is both the `:substitute` command and the `substitute()` function. When the ! line starts with `substitute(` this will use the function. Prepend a colon to ! use the command instead: > :substitute(pattern (replacement ( Note that while variables need to be defined before they can be used, *************** *** 261,268 **** Automatic line continuation ~ In many cases it is obvious that an expression continues on the next line. In ! those cases there is no need to prefix the line with a backslash. For ! example, when a list spans multiple lines: > let mylist = [ 'one', 'two', --- 261,268 ---- Automatic line continuation ~ In many cases it is obvious that an expression continues on the next line. In ! those cases there is no need to prefix the line with a backslash ! |line-continuation|. For example, when a list spans multiple lines: > let mylist = [ 'one', 'two', *************** *** 452,457 **** --- 452,462 ---- 'text'->func() " Vim9: method call :'t " legacy Vim: jump to mark m + Some Ex commands can be confused with assignments in Vim9 script: > + g:name = value # assignment + g:pattern:cmd # invalid command - ERROR + :g:pattern:cmd # :global command + Functions defined with `:def` compile the whole function. Legacy functions can bail out, and the following lines are not parsed: > func Maybe() *************** *** 614,619 **** --- 619,626 ---- 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: > *************** *** 640,645 **** --- 647,658 ---- 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 ============================================================================== *** ../vim-8.2.1406/src/version.c 2020-08-09 16:36:45.870516749 +0200 --- src/version.c 2020-08-09 16:58:09.123410307 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1407, /**/ -- You're as much use as a condom machine at the Vatican. -- Rimmer to Holly in Red Dwarf 'Queeg' /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///