]> granicus.if.org Git - vim/commitdiff
patch 8.2.0508: Vim9: func and partial types not done yet v8.2.0508
authorBram Moolenaar <Bram@vim.org>
Fri, 3 Apr 2020 19:59:57 +0000 (21:59 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 3 Apr 2020 19:59:57 +0000 (21:59 +0200)
Problem:    Vim9: func and partial types not done yet
Solution:   Fill in details about func declaration, drop a separate partial
            declaration.

runtime/doc/vim9.txt
src/evalfunc.c
src/globals.h
src/structs.h
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9compile.c

index 9128b62e75c951dfcb4c360d98702a7ce3d7485b..1c180708aef60fc1b48d6bfc3d04784ca8663078 100644 (file)
@@ -1,4 +1,4 @@
-*vim9.txt*     For Vim version 8.2.  Last change: 2020 Mar 01
+*vim9.txt*     For Vim version 8.2.  Last change: 2020 Apr 03
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -249,8 +249,8 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
                        the function follows in the next lines, until the
                        matching `:enddef`.
 
-                       When {return-type} is omitted the function is not
-                       expected to return anything.
+                       When {return-type} is omitted or is "void" the
+                       function is not expected to return anything.
                        
                        {arguments} is a sequence of zero or more argument
                        declarations.  There are three forms:
@@ -296,29 +296,40 @@ The following builtin types are supported:
        float
        string
        blob
-       list<type>
-       dict<type>
-       (a: type, b: type): type
+       list<{type}>
+       dict<{type}>
        job
        channel
        func
-       partial
+       func({type}, ...)
+       func({type}, ...): {type}
 
 Not supported yet:
-       tuple<a: type, b: type, ...>
+       tuple<a: {type}, b: {type}, ...>
 
-These types can be used in declarations, but no variable will have this type:
-       type|type
+These types can be used in declarations, but no value will have this type:
+       {type}|{type}
        void
        any
 
-There is no array type, use list<type> instead.  For a list constant an
+There is no array type, use list<{type}> instead.  For a list constant an
 efficient implementation is used that avoids allocating lot of small pieces of
 memory.
 
-A function defined with `:def` must declare the return type.  If there is no
-type then the function doesn't return anything.  "void" is used in type
-declarations.
+A partial and function can be declared in more or less specific ways:
+func                           any kind of function reference, no type
+                               checking
+func: {type}                   any number and type of arguments with specific
+                               return type
+func({type} ...)               function with argument types, does not return
+                               a value
+func({type} ...): {type}       function with argument types and return type
+
+If the return type is "void" the function does not return a value.
+
+The reference can also be a |Partial|, in which case it stores extra arguments
+and/or a dictionary, which are not visible to the caller.  Since they are
+called in the same way the declaration is the same.
 
 Custom types can be defined with `:type`: >
        :type MyList list<string>
index 0db848cb359889bb0920b5ca8d237ad3f22630d6..f43ac082c1e45769a94268fb62af2630f78a2d55 100644 (file)
@@ -336,11 +336,6 @@ ret_func_any(int argcount UNUSED, type_T **argtypes UNUSED)
     return &t_func_any;
 }
     static type_T *
-ret_partial_any(int argcount UNUSED, type_T **argtypes UNUSED)
-{
-    return &t_partial_any;
-}
-    static type_T *
 ret_channel(int argcount UNUSED, type_T **argtypes UNUSED)
 {
     return &t_channel;
@@ -564,7 +559,7 @@ static funcentry_T global_functions[] =
     {"foldtext",       0, 0, 0,          ret_string,   f_foldtext},
     {"foldtextresult", 1, 1, FEARG_1,    ret_string,   f_foldtextresult},
     {"foreground",     0, 0, 0,          ret_void,     f_foreground},
-    {"funcref",                1, 3, FEARG_1,    ret_partial_any, f_funcref},
+    {"funcref",                1, 3, FEARG_1,    ret_func_any, f_funcref},
     {"function",       1, 3, FEARG_1,    ret_f_function, f_function},
     {"garbagecollect", 0, 1, 0,          ret_void,     f_garbagecollect},
     {"get",            2, 3, FEARG_1,    ret_any,      f_get},
@@ -961,7 +956,7 @@ static funcentry_T global_functions[] =
     {"test_null_function", 0, 0, 0,      ret_func_any, f_test_null_function},
     {"test_null_job",  0, 0, 0,          ret_job,      JOB_FUNC(f_test_null_job)},
     {"test_null_list", 0, 0, 0,          ret_list_any, f_test_null_list},
-    {"test_null_partial", 0, 0, 0,       ret_partial_any, f_test_null_partial},
+    {"test_null_partial", 0, 0, 0,       ret_func_any, f_test_null_partial},
     {"test_null_string", 0, 0, 0,        ret_string,   f_test_null_string},
     {"test_option_not_set", 1, 1, FEARG_1,ret_void,     f_test_option_not_set},
     {"test_override",  2, 2, FEARG_2,    ret_void,     f_test_override},
@@ -2902,7 +2897,7 @@ ret_f_function(int argcount, type_T **argtypes UNUSED)
 {
     if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING)
        return &t_func_any;
-    return &t_partial_void;
+    return &t_func_void;
 }
 
 /*
index ac48a793d1edf6236d2de5af6860f1da1b7d930e..59f22d1af85af2efa8b18e520da60638c164b120 100644 (file)
@@ -379,36 +379,39 @@ EXTERN sctx_T     current_sctx INIT4(0, 0, 0, 0);
 
 
 // Commonly used types.
-EXTERN type_T t_any INIT4(VAR_UNKNOWN, 0, NULL, NULL);
-EXTERN type_T t_void INIT4(VAR_VOID, 0, NULL, NULL);
-EXTERN type_T t_bool INIT4(VAR_BOOL, 0, NULL, NULL);
-EXTERN type_T t_special INIT4(VAR_SPECIAL, 0, NULL, NULL);
-EXTERN type_T t_number INIT4(VAR_NUMBER, 0, NULL, NULL);
-EXTERN type_T t_float INIT4(VAR_FLOAT, 0, NULL, NULL);
-EXTERN type_T t_string INIT4(VAR_STRING, 0, NULL, NULL);
-EXTERN type_T t_blob INIT4(VAR_BLOB, 0, NULL, NULL);
-EXTERN type_T t_job INIT4(VAR_JOB, 0, NULL, NULL);
-EXTERN type_T t_channel INIT4(VAR_CHANNEL, 0, NULL, NULL);
-
-EXTERN type_T t_func_void INIT4(VAR_FUNC, -1, &t_void, NULL);
-EXTERN type_T t_func_any INIT4(VAR_FUNC, -1, &t_any, NULL);
-
-EXTERN type_T t_partial_void INIT4(VAR_PARTIAL, -1, &t_void, NULL);
-EXTERN type_T t_partial_any INIT4(VAR_PARTIAL, -1, &t_any, NULL);
-
-EXTERN type_T t_list_any INIT4(VAR_LIST, 0, &t_any, NULL);
-EXTERN type_T t_dict_any INIT4(VAR_DICT, 0, &t_any, NULL);
-EXTERN type_T t_list_empty INIT4(VAR_LIST, 0, &t_void, NULL);
-EXTERN type_T t_dict_empty INIT4(VAR_DICT, 0, &t_void, NULL);
-
-EXTERN type_T t_list_bool INIT4(VAR_LIST, 0, &t_bool, NULL);
-EXTERN type_T t_list_number INIT4(VAR_LIST, 0, &t_number, NULL);
-EXTERN type_T t_list_string INIT4(VAR_LIST, 0, &t_string, NULL);
-EXTERN type_T t_list_dict_any INIT4(VAR_LIST, 0, &t_dict_any, NULL);
-
-EXTERN type_T t_dict_bool INIT4(VAR_DICT, 0, &t_bool, NULL);
-EXTERN type_T t_dict_number INIT4(VAR_DICT, 0, &t_number, NULL);
-EXTERN type_T t_dict_string INIT4(VAR_DICT, 0, &t_string, NULL);
+EXTERN type_T t_any INIT5(VAR_UNKNOWN, 0, 0, NULL, NULL);
+EXTERN type_T t_void INIT5(VAR_VOID, 0, 0, NULL, NULL);
+EXTERN type_T t_bool INIT5(VAR_BOOL, 0, 0, NULL, NULL);
+EXTERN type_T t_special INIT5(VAR_SPECIAL, 0, 0, NULL, NULL);
+EXTERN type_T t_number INIT5(VAR_NUMBER, 0, 0, NULL, NULL);
+EXTERN type_T t_float INIT5(VAR_FLOAT, 0, 0, NULL, NULL);
+EXTERN type_T t_string INIT5(VAR_STRING, 0, 0, NULL, NULL);
+EXTERN type_T t_blob INIT5(VAR_BLOB, 0, 0, NULL, NULL);
+EXTERN type_T t_job INIT5(VAR_JOB, 0, 0, NULL, NULL);
+EXTERN type_T t_channel INIT5(VAR_CHANNEL, 0, 0, NULL, NULL);
+
+EXTERN type_T t_func_void INIT5(VAR_FUNC, -1, 0, &t_void, NULL);
+EXTERN type_T t_func_any INIT5(VAR_FUNC, -1, 0, &t_any, NULL);
+EXTERN type_T t_func_number INIT5(VAR_FUNC, -1, 0, &t_number, NULL);
+EXTERN type_T t_func_string INIT5(VAR_FUNC, -1, 0, &t_string, NULL);
+EXTERN type_T t_func_0_void INIT5(VAR_FUNC, 0, 0, &t_void, NULL);
+EXTERN type_T t_func_0_any INIT5(VAR_FUNC, 0, 0, &t_any, NULL);
+EXTERN type_T t_func_0_number INIT5(VAR_FUNC, 0, 0, &t_number, NULL);
+EXTERN type_T t_func_0_string INIT5(VAR_FUNC, 0, 0, &t_string, NULL);
+
+EXTERN type_T t_list_any INIT5(VAR_LIST, 0, 0, &t_any, NULL);
+EXTERN type_T t_dict_any INIT5(VAR_DICT, 0, 0, &t_any, NULL);
+EXTERN type_T t_list_empty INIT5(VAR_LIST, 0, 0, &t_void, NULL);
+EXTERN type_T t_dict_empty INIT5(VAR_DICT, 0, 0, &t_void, NULL);
+
+EXTERN type_T t_list_bool INIT5(VAR_LIST, 0, 0, &t_bool, NULL);
+EXTERN type_T t_list_number INIT5(VAR_LIST, 0, 0, &t_number, NULL);
+EXTERN type_T t_list_string INIT5(VAR_LIST, 0, 0, &t_string, NULL);
+EXTERN type_T t_list_dict_any INIT5(VAR_LIST, 0, 0, &t_dict_any, NULL);
+
+EXTERN type_T t_dict_bool INIT5(VAR_DICT, 0, 0, &t_bool, NULL);
+EXTERN type_T t_dict_number INIT5(VAR_DICT, 0, 0, &t_number, NULL);
+EXTERN type_T t_dict_string INIT5(VAR_DICT, 0, 0, &t_string, NULL);
 
 
 #endif
index 9f3628e3ce4b67ea283e9b735ce1e380ebfe5243..efc74da175a5529deac88bf62bf30a13fd6067dd 100644 (file)
@@ -1342,10 +1342,14 @@ typedef struct type_S type_T;
 struct type_S {
     vartype_T      tt_type;
     short          tt_argcount;    // for func, partial, -1 for unknown
+    short          tt_flags;       // TTFLAG_ values
     type_T         *tt_member;     // for list, dict, func return type
-    type_T         *tt_args;       // func arguments
+    type_T         **tt_args;      // func arguments, allocated
 };
 
+#define TTFLAG_VARARGS 1           // func args ends with "..."
+#define TTFLAG_OPTARG  2           // func arg type with "?"
+
 /*
  * Structure to hold an internal variable without a name.
  */
index 36c098c7e97d605617055a488c38b4df2d15a7bd..e71782b970559b0e78fda552ba07f1e822d056c0 100644 (file)
@@ -362,8 +362,7 @@ enddef
 def WithFunc()
   let funky1: func
   let funky2: func = function("len")
-  let party1: partial
-  let party2: partial = funcref("UserFunc")
+  let party2: func = funcref("UserFunc")
 enddef
 
 def Test_disassemble_function()
@@ -376,15 +375,12 @@ def Test_disassemble_function()
         \ .. '2 PUSHS "len".*'
         \ .. '3 BCALL function(argc 1).*'
         \ .. '4 STORE $1.*'
-        \ .. 'let party1: partial.*'
-        \ .. '5 PUSHPARTIAL "\[none]".*'
-        \ .. '6 STORE $2.*'
-        \ .. 'let party2: partial = funcref("UserFunc").*'
-        \ .. '7 PUSHS "UserFunc".*'
-        \ .. '8 BCALL funcref(argc 1).*'
-        \ .. '9 STORE $3.*'
-        \ .. '10 PUSHNR 0.*'
-        \ .. '11 RETURN.*'
+        \ .. 'let party2: func = funcref("UserFunc").*'
+        \ .. '\d PUSHS "UserFunc".*'
+        \ .. '\d BCALL funcref(argc 1).*'
+        \ .. '\d STORE $2.*'
+        \ .. '\d PUSHNR 0.*'
+        \ .. '\d RETURN.*'
         \, instr)
 enddef
 
@@ -753,10 +749,10 @@ def Test_disassemble_compare()
         \ ['#{a:1} is #{x:2}', 'COMPAREDICT is'],
         \ ['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
         \
-        \ ['{->33} == {->44}', 'COMPAREPARTIAL =='],
-        \ ['{->33} != {->44}', 'COMPAREPARTIAL !='],
-        \ ['{->33} is {->44}', 'COMPAREPARTIAL is'],
-        \ ['{->33} isnot {->44}', 'COMPAREPARTIAL isnot'],
+        \ ['{->33} == {->44}', 'COMPAREFUNC =='],
+        \ ['{->33} != {->44}', 'COMPAREFUNC !='],
+        \ ['{->33} is {->44}', 'COMPAREFUNC is'],
+        \ ['{->33} isnot {->44}', 'COMPAREFUNC isnot'],
         \
         \ ['77 == g:xx', 'COMPAREANY =='],
         \ ['77 != g:xx', 'COMPAREANY !='],
index 8f27c6d8488a3f2298e621e18818e4eddc67f89b..d9c20acb1880d720346675b892653b0200c9afe0 100644 (file)
@@ -461,7 +461,7 @@ func Test_expr4_fails()
   call CheckDefFailureMult(['let j: job', 'let chan: channel', 'let r = j == chan'], 'Cannot compare job with channel')
   call CheckDefFailureMult(['let j: job', 'let x: list<any>', 'let r = j == x'], 'Cannot compare job with list')
   call CheckDefFailureMult(['let j: job', 'let x: func', 'let r = j == x'], 'Cannot compare job with func')
-  call CheckDefFailureMult(['let j: job', 'let x: partial', 'let r = j == x'], 'Cannot compare job with partial')
+  call CheckDefFailureMult(['let j: job', 'let x: func', 'let r = j == x'], 'Cannot compare job with func')
 endfunc
 
 " test addition, subtraction, concatenation
index 518f60c62c7a2223bf72fa40f31b157be8b5f76f..cf12e85bb7f56c07953b508e629621417274c5f9 100644 (file)
@@ -68,8 +68,7 @@ def Test_assignment()
   endif
   let funky1: func
   let funky2: func = function('len')
-  let party1: partial
-  let party2: partial = funcref('Test_syntax')
+  let party2: func = funcref('Test_syntax')
 
   " type becomes list<any>
   let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
@@ -157,9 +156,6 @@ def Test_assignment()
   let thefunc: func
   assert_equal(test_null_function(), thefunc)
 
-  let thepartial: partial
-  assert_equal(test_null_partial(), thepartial)
-
   let thelist: list<any>
   assert_equal([], thelist)
 
@@ -213,7 +209,7 @@ func Test_assignment_failure()
   call CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
   call CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
 
-  call CheckDefFailure(['let var: dict <number>'], 'E1007:')
+  call CheckDefFailure(['let var: dict <number>'], 'E1068:')
   call CheckDefFailure(['let var: dict<number'], 'E1009:')
 endfunc
 
index 38ab5315502419b9d93e635abce6b06257558681..f0823995e3f439c3a3b38a722fa4b183cf8ad916 100644 (file)
@@ -738,6 +738,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    508,
 /**/
     507,
 /**/
index 3ce04a14a9154dbad138e93fd22f8633169e5ad6..b64f8efd58ec47585c090193d68f9fb46ea67a14 100644 (file)
@@ -224,7 +224,7 @@ check_defined(char_u *p, int len, cctx_T *cctx)
 }
 
     static type_T *
-get_list_type(type_T *member_type, garray_T *type_list)
+get_list_type(type_T *member_type, garray_T *type_gap)
 {
     type_T *type;
 
@@ -241,17 +241,19 @@ get_list_type(type_T *member_type, garray_T *type_list)
        return &t_list_string;
 
     // Not a common type, create a new entry.
-    if (ga_grow(type_list, 1) == FAIL)
+    if (ga_grow(type_gap, 1) == FAIL)
        return &t_any;
-    type = ((type_T *)type_list->ga_data) + type_list->ga_len;
-    ++type_list->ga_len;
+    type = ((type_T *)type_gap->ga_data) + type_gap->ga_len;
+    ++type_gap->ga_len;
     type->tt_type = VAR_LIST;
     type->tt_member = member_type;
+    type->tt_argcount = 0;
+    type->tt_args = NULL;
     return type;
 }
 
     static type_T *
-get_dict_type(type_T *member_type, garray_T *type_list)
+get_dict_type(type_T *member_type, garray_T *type_gap)
 {
     type_T *type;
 
@@ -268,12 +270,68 @@ get_dict_type(type_T *member_type, garray_T *type_list)
        return &t_dict_string;
 
     // Not a common type, create a new entry.
-    if (ga_grow(type_list, 1) == FAIL)
+    if (ga_grow(type_gap, 1) == FAIL)
        return &t_any;
-    type = ((type_T *)type_list->ga_data) + type_list->ga_len;
-    ++type_list->ga_len;
+    type = ((type_T *)type_gap->ga_data) + type_gap->ga_len;
+    ++type_gap->ga_len;
     type->tt_type = VAR_DICT;
     type->tt_member = member_type;
+    type->tt_argcount = 0;
+    type->tt_args = NULL;
+    return type;
+}
+
+/*
+ * Get a function type, based on the return type "ret_type".
+ * If "argcount" is -1 or 0 a predefined type can be used.
+ * If "argcount" > 0 always create a new type, so that arguments can be added.
+ */
+    static type_T *
+get_func_type(type_T *ret_type, int argcount, garray_T *type_gap)
+{
+    type_T *type;
+
+    // recognize commonly used types
+    if (argcount <= 0)
+    {
+       if (ret_type == &t_void)
+       {
+           if (argcount == 0)
+               return &t_func_0_void;
+           else
+               return &t_func_void;
+       }
+       if (ret_type == &t_any)
+       {
+           if (argcount == 0)
+               return &t_func_0_any;
+           else
+               return &t_func_any;
+       }
+       if (ret_type == &t_number)
+       {
+           if (argcount == 0)
+               return &t_func_0_number;
+           else
+               return &t_func_number;
+       }
+       if (ret_type == &t_string)
+       {
+           if (argcount == 0)
+               return &t_func_0_string;
+           else
+               return &t_func_string;
+       }
+    }
+
+    // Not a common type or has arguments, create a new entry.
+    if (ga_grow(type_gap, 1) == FAIL)
+       return &t_any;
+    type = ((type_T *)type_gap->ga_data) + type_gap->ga_len;
+    ++type_gap->ga_len;
+    type->tt_type = VAR_FUNC;
+    type->tt_member = ret_type;
+    type->tt_args = NULL;
     return type;
 }
 
@@ -774,8 +832,7 @@ generate_PUSHPARTIAL(cctx_T *cctx, partial_T *part)
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr_type(cctx, ISN_PUSHPARTIAL,
-                                                     &t_partial_any)) == NULL)
+    if ((isn = generate_instr_type(cctx, ISN_PUSHPARTIAL, &t_func_any)) == NULL)
        return FAIL;
     isn->isn_arg.partial = part;
 
@@ -942,7 +999,6 @@ generate_NEWLIST(cctx_T *cctx, int count)
 {
     isn_T      *isn;
     garray_T   *stack = &cctx->ctx_type_stack;
-    garray_T   *type_list = cctx->ctx_type_list;
     type_T     *type;
     type_T     *member;
 
@@ -960,7 +1016,7 @@ generate_NEWLIST(cctx_T *cctx, int count)
        member = ((type_T **)stack->ga_data)[stack->ga_len];
     else
        member = &t_void;
-    type = get_list_type(member, type_list);
+    type = get_list_type(member, cctx->ctx_type_list);
 
     // add the list type to the type stack
     if (ga_grow(stack, 1) == FAIL)
@@ -979,7 +1035,6 @@ generate_NEWDICT(cctx_T *cctx, int count)
 {
     isn_T      *isn;
     garray_T   *stack = &cctx->ctx_type_stack;
-    garray_T   *type_list = cctx->ctx_type_list;
     type_T     *type;
     type_T     *member;
 
@@ -997,7 +1052,7 @@ generate_NEWDICT(cctx_T *cctx, int count)
        member = ((type_T **)stack->ga_data)[stack->ga_len + 1];
     else
        member = &t_void;
-    type = get_dict_type(member, type_list);
+    type = get_dict_type(member, cctx->ctx_type_list);
 
     // add the dict type to the type stack
     if (ga_grow(stack, 1) == FAIL)
@@ -1024,7 +1079,7 @@ generate_FUNCREF(cctx_T *cctx, int dfunc_idx)
 
     if (ga_grow(stack, 1) == FAIL)
        return FAIL;
-    ((type_T **)stack->ga_data)[stack->ga_len] = &t_partial_any;
+    ((type_T **)stack->ga_data)[stack->ga_len] = &t_func_any;
     // TODO: argument and return types
     ++stack->ga_len;
 
@@ -1298,6 +1353,8 @@ generate_EXEC(cctx_T *cctx, char_u *line)
 
 static char e_white_both[] =
                        N_("E1004: white space required before and after '%s'");
+static char e_white_after[] = N_("E1069: white space required after '%s'");
+static char e_no_white_before[] = N_("E1068: No white space allowed before '%s'");
 
 /*
  * Reserve space for a local variable.
@@ -1385,11 +1442,11 @@ skip_type(char_u *start)
 
 /*
  * Parse the member type: "<type>" and return "type" with the member set.
- * Use "type_list" if a new type needs to be added.
+ * Use "type_gap" if a new type needs to be added.
  * Returns NULL in case of failure.
  */
     static type_T *
-parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
+parse_type_member(char_u **arg, type_T *type, garray_T *type_gap)
 {
     type_T  *member_type;
     int            prev_called_emsg = called_emsg;
@@ -1397,14 +1454,14 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
     if (**arg != '<')
     {
        if (*skipwhite(*arg) == '<')
-           emsg(_("E1007: No white space allowed before <"));
+           semsg(_(e_no_white_before), "<");
        else
            emsg(_("E1008: Missing <type>"));
        return type;
     }
     *arg = skipwhite(*arg + 1);
 
-    member_type = parse_type(arg, type_list);
+    member_type = parse_type(arg, type_gap);
 
     *arg = skipwhite(*arg);
     if (**arg != '>' && called_emsg == prev_called_emsg)
@@ -1415,8 +1472,8 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
     ++*arg;
 
     if (type->tt_type == VAR_LIST)
-       return get_list_type(member_type, type_list);
-    return get_dict_type(member_type, type_list);
+       return get_list_type(member_type, type_gap);
+    return get_dict_type(member_type, type_gap);
 }
 
 /*
@@ -1424,7 +1481,7 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_list)
  * Return &t_any for failure.
  */
     type_T *
-parse_type(char_u **arg, garray_T *type_list)
+parse_type(char_u **arg, garray_T *type_gap)
 {
     char_u  *p = *arg;
     size_t  len;
@@ -1466,7 +1523,7 @@ parse_type(char_u **arg, garray_T *type_list)
            if (len == 4 && STRNCMP(*arg, "dict", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_dict_any, type_list);
+               return parse_type_member(arg, &t_dict_any, type_gap);
            }
            break;
        case 'f':
@@ -1482,9 +1539,85 @@ parse_type(char_u **arg, garray_T *type_list)
            }
            if (len == 4 && STRNCMP(*arg, "func", len) == 0)
            {
+               type_T  *type;
+               type_T  *ret_type = &t_void;
+               int     argcount = -1;
+               int     flags = 0;
+               type_T  *arg_type[MAX_FUNC_ARGS + 1];
+
+               // func({type}, ...): {type}
                *arg += len;
-               // TODO: arguments and return type
-               return &t_func_any;
+               if (**arg == '(')
+               {
+                   p = ++*arg;
+                   argcount = 0;
+                   while (*p != NUL && *p != ')')
+                   {
+                       if (STRNCMP(p, "...", 3) == 0)
+                       {
+                           flags |= TTFLAG_VARARGS;
+                           break;
+                       }
+                       arg_type[argcount++] = parse_type(&p, type_gap);
+
+                       if (*p != ',' && *skipwhite(p) == ',')
+                       {
+                           semsg(_(e_no_white_before), ",");
+                           return &t_any;
+                       }
+                       if (*p == ',')
+                       {
+                           ++p;
+                           if (!VIM_ISWHITE(*p))
+                               semsg(_(e_white_after), ",");
+                       }
+                       p = skipwhite(p);
+                       if (argcount == MAX_FUNC_ARGS)
+                       {
+                           emsg(_("E740: Too many argument types"));
+                           return &t_any;
+                       }
+                   }
+
+                   p = skipwhite(p);
+                   if (*p != ')')
+                   {
+                       emsg(_(e_missing_close));
+                       return &t_any;
+                   }
+                   *arg = p + 1;
+               }
+               if (**arg == ':')
+               {
+                   // parse return type
+                   ++*arg;
+                   if (!VIM_ISWHITE(*p))
+                       semsg(_(e_white_after), ":");
+                   *arg = skipwhite(*arg);
+                   ret_type = parse_type(arg, type_gap);
+               }
+               type = get_func_type(ret_type, flags == 0 ? argcount : 99,
+                                                                   type_gap);
+               if (flags != 0)
+                   type->tt_flags = flags;
+               if (argcount > 0)
+               {
+                   int type_ptr_cnt = (sizeof(type_T *) * argcount
+                                       + sizeof(type_T) - 1) / sizeof(type_T);
+
+                   type->tt_argcount = argcount;
+                   // Get space from "type_gap" to avoid having to keep track
+                   // of the pointer and freeing it.
+                   ga_grow(type_gap, type_ptr_cnt);
+                   if (ga_grow(type_gap, type_ptr_cnt) == FAIL)
+                       return &t_any;
+                   type->tt_args =
+                            ((type_T **)type_gap->ga_data) + type_gap->ga_len;
+                   type_gap->ga_len += type_ptr_cnt;
+                   mch_memmove(type->tt_args, arg_type,
+                                                 sizeof(type_T *) * argcount);
+               }
+               return type;
            }
            break;
        case 'j':
@@ -1498,7 +1631,7 @@ parse_type(char_u **arg, garray_T *type_list)
            if (len == 4 && STRNCMP(*arg, "list", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_list_any, type_list);
+               return parse_type_member(arg, &t_list_any, type_gap);
            }
            break;
        case 'n':
@@ -1508,14 +1641,6 @@ parse_type(char_u **arg, garray_T *type_list)
                return &t_number;
            }
            break;
-       case 'p':
-           if (len == 7 && STRNCMP(*arg, "partial", len) == 0)
-           {
-               *arg += len;
-               // TODO: arguments and return type
-               return &t_partial_any;
-           }
-           break;
        case 's':
            if (len == 6 && STRNCMP(*arg, "string", len) == 0)
            {
@@ -1574,7 +1699,7 @@ equal_type(type_T *type1, type_T *type2)
  * "type2" and "dest" may be the same.
  */
     static void
-common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_list)
+common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
 {
     if (equal_type(type1, type2))
     {
@@ -1588,11 +1713,11 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_list)
        {
            type_T *common;
 
-           common_type(type1->tt_member, type2->tt_member, &common, type_list);
+           common_type(type1->tt_member, type2->tt_member, &common, type_gap);
            if (type1->tt_type == VAR_LIST)
-               *dest = get_list_type(common, type_list);
+               *dest = get_list_type(common, type_gap);
            else
-               *dest = get_dict_type(common, type_list);
+               *dest = get_dict_type(common, type_gap);
            return;
        }
        // TODO: VAR_FUNC and VAR_PARTIAL
@@ -1962,14 +2087,14 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount)
 
        if (*p != ',' && *skipwhite(p) == ',')
        {
-           emsg(_("E1068: No white space allowed before ,"));
+           semsg(_(e_no_white_before), ",");
            p = skipwhite(p);
        }
        if (*p == ',')
        {
            ++p;
            if (!VIM_ISWHITE(*p))
-               emsg(_("E1069: white space required after ,"));
+               semsg(_(e_white_after), ",");
        }
        p = skipwhite(p);
     }