]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1577: Vim9: no generic support yet v9.1.1577
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 21 Jul 2025 19:36:08 +0000 (21:36 +0200)
committerChristian Brabandt <cb@256bit.org>
Mon, 21 Jul 2025 19:36:08 +0000 (21:36 +0200)
Problem:  Vim9: no generic support yet
Solution: Add support for generic functions, funcrefs and object/class
          methods (Yegappan Lakshmanan).

closes: #17313

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
35 files changed:
Filelist
runtime/doc/tags
runtime/doc/todo.txt
runtime/doc/version9.txt
runtime/doc/vim9.txt
src/Make_ami.mak
src/Make_cyg_ming.mak
src/Make_mvc.mak
src/Make_vms.mms
src/Makefile
src/errors.h
src/eval.c
src/evalfunc.c
src/evalvars.c
src/ex_docmd.c
src/po/vim.pot
src/proto.h
src/proto/userfunc.pro
src/proto/vim9expr.pro
src/proto/vim9generics.pro [new file with mode: 0644]
src/proto/vim9type.pro
src/structs.h
src/testdir/Make_all.mak
src/testdir/test_vim9_generics.vim [new file with mode: 0644]
src/userfunc.c
src/version.c
src/vim9class.c
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9generics.c [new file with mode: 0644]
src/vim9instr.c
src/vim9script.c
src/vim9type.c

index 41eba31075bde6a8df9ca90a61338210e8a84e41..5153ded57d0c3571c6bf37a46a0aadbcba3abac6 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -177,6 +177,7 @@ SRC_ALL =   \
                src/vim9compile.c \
                src/vim9execute.c \
                src/vim9expr.c \
+               src/vim9generics.c \
                src/vim9instr.c \
                src/vim9script.c \
                src/vim9type.c \
@@ -362,6 +363,7 @@ SRC_ALL =   \
                src/proto/vim9compile.pro \
                src/proto/vim9execute.pro \
                src/proto/vim9expr.pro \
+               src/proto/vim9generics.pro \
                src/proto/vim9instr.pro \
                src/proto/vim9script.pro \
                src/proto/vim9type.pro \
index 5535036b2e22b8dfbffddc25f59ff37992da2974..69811f999693244845d39cdc798617e78aa59977 100644 (file)
@@ -4615,6 +4615,9 @@ E1425     vim9class.txt   /*E1425*
 E1426  vim9class.txt   /*E1426*
 E1429  vim9class.txt   /*E1429*
 E143   autocmd.txt     /*E143*
+E1432  vim9.txt        /*E1432*
+E1433  vim9.txt        /*E1433*
+E1434  vim9.txt        /*E1434*
 E144   various.txt     /*E144*
 E145   starting.txt    /*E145*
 E146   change.txt      /*E146*
@@ -4674,7 +4677,17 @@ E1548    wayland.txt     /*E1548*
 E1549  options.txt     /*E1549*
 E155   sign.txt        /*E155*
 E1550  options.txt     /*E1550*
+E1552  vim9.txt        /*E1552*
+E1553  vim9.txt        /*E1553*
+E1554  vim9.txt        /*E1554*
+E1555  vim9.txt        /*E1555*
+E1556  vim9.txt        /*E1556*
+E1557  vim9.txt        /*E1557*
+E1558  vim9.txt        /*E1558*
+E1559  vim9.txt        /*E1559*
 E156   sign.txt        /*E156*
+E1560  vim9.txt        /*E1560*
+E1561  vim9.txt        /*E1561*
 E157   sign.txt        /*E157*
 E158   sign.txt        /*E158*
 E159   sign.txt        /*E159*
@@ -7963,6 +7976,9 @@ gdb       debug.txt       /*gdb*
 gdb-version    terminal.txt    /*gdb-version*
 ge     motion.txt      /*ge*
 gender-neutral helphelp.txt    /*gender-neutral*
+generic-function-call  vim9.txt        /*generic-function-call*
+generic-function-declaration   vim9.txt        /*generic-function-declaration*
+generic-functions      vim9.txt        /*generic-functions*
 get()  builtin.txt     /*get()*
 get()-blob     builtin.txt     /*get()-blob*
 get()-dict     builtin.txt     /*get()-dict*
@@ -11023,6 +11039,7 @@ type-casting    vim9.txt        /*type-casting*
 type-checking  vim9.txt        /*type-checking*
 type-inference vim9.txt        /*type-inference*
 type-mistakes  tips.txt        /*type-mistakes*
+type-variable-naming   vim9.txt        /*type-variable-naming*
 typealias      vim9class.txt   /*typealias*
 typename()     builtin.txt     /*typename()*
 typescript.vim syntax.txt      /*typescript.vim*
index 0a4c92fcb80cd0a5dfdbbaea420699d296b52260..ff6b6a59ec38c0ac505cb8f2b52a0c02c95619a0 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2025 Jun 12
+*todo.txt*      For Vim version 9.1.  Last change: 2025 Jul 21
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -115,7 +115,6 @@ Further Vim9 improvements:
   - For chaining, allow using the class name as type for function return
     value.
   - Implement "specifies" interface
-  - Implement generics
   - Add "assignable" (class or child)?
   - More efficient way for interface member index than iterating over list?
   - a variant of type() that returns a different type for each class?
index 79aec13adc0797261a98e2bd046fbafec4325d22..10b47424fa00caeffba4c3a780037ad8e3a591de 100644 (file)
@@ -41551,6 +41551,8 @@ Add support for internal builtin functions with vim9 objects, see
 
 Enum support for Vim9 script |:enum|
 
+Generic function support for Vim9 script |generic-functions|
+
 Support for protected _new() method
 
 Support for compiling all the methods in a Vim9 class using |:defcompile|.
index 0dae57debc8c6d77d54e6f66e62f7eb7a7946c3f..063c179364f74f72764189127fdbf571a6fe17f3 100644 (file)
@@ -1,4 +1,4 @@
-*vim9.txt*     For Vim version 9.1.  Last change: 2025 Apr 27
+*vim9.txt*     For Vim version 9.1.  Last change: 2025 Jul 21
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -15,10 +15,11 @@ features in Vim9 script.
 2.  Differences                                |vim9-differences|
 3.  New style functions                        |fast-functions|
 4.  Types                              |vim9-types|
-5.  Namespace, Import and Export       |vim9script|
-6.  Classes and interfaces             |vim9-classes|
+5.  Generic functions                  |generic-functions|
+6.  Namespace, Import and Export       |vim9script|
+7.  Classes and interfaces             |vim9-classes|
 
-9.  Rationale                          |vim9-rationale|
+8.  Rationale                          |vim9-rationale|
 
 ==============================================================================
 
@@ -1895,7 +1896,146 @@ corresponding empty value.
 
 ==============================================================================
 
-5. Namespace, Import and Export
+                                               *generic-functions*
+5. Generic functions
+
+A generic function allows using the same function with different type
+arguments, while retaining type checking for arguments and the return value.
+This provides type safety and code reusability.
+
+Declaration~
+                                               *generic-function-declaration*
+                                               *E1553* *E1554* *E1559*
+The type parameters for a generic function are declared in angle brackets "<"
+and ">" directly after the function name.  Multiple type names are separated
+by commas: >
+
+    def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}]
+       {function body}
+    enddef
+<
+These type parameters can then be used like any other type within the function
+signature and body.  Example: >
+
+    def MyFunc<T, A, B>(param1: T): T
+       var f: A
+       var x = param1
+       return x
+    enddef
+<
+                                               *type-variable-naming* *E1552*
+The convention is to use a single uppercase letter for a type variable (e.g.,
+T, A, X), although longer names are allowed.  The name must start with an
+uppercase letter.
+
+                                               *E1558* *E1560*
+A function must be declared and used either as generic or as a regular
+function - but not both.
+
+                                               *E1561*
+A type variable name must not conflict with other defined names, such as class
+names, type aliases, enum names, function names or other type variable names.
+
+Calling a generic function~
+                                               *generic-function-call*
+To call a generic function, specify the concrete types in "<" and ">"
+between the function name and the argument list: >
+
+    MyFunc<number, string, list<number>>()
+<
+                                               *E1555* *E1556* *E1557*
+The number of concrete types provided when calling a generic function must
+match the number of type variables in the function.  An empty type list is not
+allowed.  Any Vim9 type (|vim9-types|) can be used as a concrete type in a
+generic function.
+
+Spaces are not allowed between the function name and "<", or between ">" and
+the opening "(".
+
+A generic function can be exported and imported like a regular function.
+See |:export| and |:import|.
+
+A generic function can be defined inside another regular or generic function.
+
+Referencing type variables in generic types~
+
+Instead of concrete types, type variables can be used with generic types.
+This is useful for complex data structures like lists of dictionaries or
+dictionaries of lists.  Example: >
+
+    vim9script
+
+    def Flatten<T>(x: list<list<T>>): list<T>
+       var result: list<T> = []
+       for inner in x
+           result += inner
+       endfor
+       return result
+    enddef
+
+    echo Flatten<number>([[1, 2], [3]])
+<
+
+Generic class method~
+
+A Vim9 class method can be a generic function: >
+
+    class A
+       def Foo<X, Y>()
+       enddef
+    endclass
+    var a = A.new()
+    a.Foo<number, string>()
+<
+                                               *E1432* *E1433* *E1434*
+A generic class method in a base class can be overridden by a generic method
+in a child class. The number of type variables must match between both
+methods.  A concrete class method cannot be overridden by a generic method,
+and vice versa.
+
+Generic function reference~
+
+A function reference (|Funcref|) can be a generic function.  This allows for
+creating factories of functions that operate on specific types: >
+
+    vim9script
+
+    def MakeEcho<T>(): func(T): T
+       return (x: T): T => x
+    enddef
+
+    var EchoNumber = MakeEcho<number>()
+    echo EchoNumber(123)
+
+    var EchoString = MakeEcho<string>()
+    echo EchoString('abc')
+<
+Compiling and Disassembling Generic functions~
+
+The |:defcompile| command can be used to compile a generic function with a
+specific list of concrete types: >
+
+    defcompile MyFunc<number, list<number>, dict<string>>
+<
+The |:disassemble| command can be used to list the instructions generated for
+a generic function: >
+
+    disassemble MyFunc<string, dict<string>>
+    disassemble MyFunc<number, list<blob>>
+<
+Limitations and Future Work~
+
+Currently, Vim does not support:
+   - Type inference for type variables: All types must be explicitly specified
+     when calling a generic function.
+   - Type constraints: It's not possible to restrict a type variable to a
+     specific class or interface (e.g., `T extends SomeInterface`).
+   - Default type arguments: Providing a default type for a type parameter
+     when not explicitly specified.
+
+==============================================================================
+
+6. Namespace, Import and Export
                                        *vim9script* *vim9-export* *vim9-import*
 
 A Vim9 script can be written to be imported.  This means that some items are
@@ -2174,7 +2314,7 @@ Or: >
 
 ==============================================================================
 
-6. Classes and interfaces                              *vim9-classes*
+7. Classes and interfaces                              *vim9-classes*
 
 In legacy script a Dictionary could be used as a kind-of object, by adding
 members that are functions.  However, this is quite inefficient and requires
@@ -2188,7 +2328,7 @@ functionality it is located in a separate help file: |vim9class.txt|.
 
 ==============================================================================
 
-9. Rationale                                           *vim9-rationale*
+8. Rationale                                           *vim9-rationale*
 
 The :def command ~
 
index bd8b525a83657371a15e97eb767811024eecca06..7c964673570cbbe3ff61975d8f03e0309f4ed2e2 100644 (file)
@@ -183,6 +183,7 @@ SRC += \
        vim9compile.c \
        vim9execute.c \
        vim9expr.c \
+       vim9generics.c \
        vim9instr.c \
        vim9script.c \
        vim9type.c \
index 37f9f6c407c6d1ee32d0defcd5248aa188afb7b3..6ae1c0a054982540d6da5e07ab6bd2c639f00a81 100644 (file)
@@ -895,6 +895,7 @@ OBJ = \
        $(OUTDIR)/vim9compile.o \
        $(OUTDIR)/vim9execute.o \
        $(OUTDIR)/vim9expr.o \
+       $(OUTDIR)/vim9generics.o \
        $(OUTDIR)/vim9instr.o \
        $(OUTDIR)/vim9script.o \
        $(OUTDIR)/vim9type.o \
@@ -1328,6 +1329,8 @@ $(OUTDIR)/vim9execute.o: vim9execute.c $(INCL) vim9.h
 
 $(OUTDIR)/vim9expr.o: vim9expr.c $(INCL) vim9.h
 
+$(OUTDIR)/vim9generics.o: vim9generics.c $(INCL) vim9.h
+
 $(OUTDIR)/vim9instr.o: vim9instr.c $(INCL) vim9.h
 
 $(OUTDIR)/vim9script.o: vim9script.c $(INCL) vim9.h
index 64b033178d31839d44ff32c0457b82be5b3c8926..f4899d56612dcc6dc7c233618aab3857f12e17d3 100644 (file)
@@ -803,6 +803,7 @@ OBJ = \
        $(OUTDIR)\vim9compile.obj \
        $(OUTDIR)\vim9execute.obj \
        $(OUTDIR)\vim9expr.obj \
+       $(OUTDIR)\vim9generics.obj \
        $(OUTDIR)\vim9instr.obj \
        $(OUTDIR)\vim9script.obj \
        $(OUTDIR)\vim9type.obj \
@@ -1824,6 +1825,8 @@ $(OUTDIR)/vim9execute.obj:        $(OUTDIR) vim9execute.c  $(INCL) vim9.h
 
 $(OUTDIR)/vim9expr.obj:        $(OUTDIR) vim9expr.c  $(INCL) vim9.h
 
+$(OUTDIR)/vim9generics.obj:    $(OUTDIR) vim9generics.c  $(INCL) vim9.h
+
 $(OUTDIR)/vim9instr.obj:       $(OUTDIR) vim9instr.c  $(INCL) vim9.h
 
 $(OUTDIR)/vim9script.obj:      $(OUTDIR) vim9script.c  $(INCL) vim9.h
@@ -2028,6 +2031,7 @@ proto.h: \
        proto/vim9compile.pro \
        proto/vim9execute.pro \
        proto/vim9expr.pro \
+       proto/vim9generics.pro \
        proto/vim9instr.pro \
        proto/vim9script.pro \
        proto/vim9type.pro \
index 9dce2b5ed24971e6c4269fe4d3830cc72740d907..fb247f3ba45d01eae295a3f98a742d6cbffb19bc 100644 (file)
@@ -446,6 +446,7 @@ SRC = \
        vim9compile.c \
        vim9execute.c \
        vim9expr.c \
+       vim9generics.c \
        vim9instr.c \
        vim9script.c \
        vim9type.c \
@@ -582,6 +583,7 @@ OBJ = \
        vim9compile.obj \
        vim9execute.obj \
        vim9expr.obj \
+       vim9generics.obj \
        vim9instr.obj \
        vim9script.obj \
        vim9type.obj \
@@ -1225,6 +1227,10 @@ vim9expr.obj : vim9expr.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
  errors.h globals.h version.h
+vim9generics.obj : vim9generics.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
+ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
+ errors.h globals.h version.h
 vim9instr.obj : vim9instr.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
index 175f659d6e45100208f4f15c19cac7385dc9c54c..e3a8600422a3d80a3a2fea14a0bd4802649a6a52 100644 (file)
@@ -1598,6 +1598,7 @@ BASIC_SRC = \
        vim9compile.c \
        vim9execute.c \
        vim9expr.c \
+       vim9generics.c \
        vim9instr.c \
        vim9script.c \
        vim9type.c \
@@ -1771,6 +1772,7 @@ OBJ_COMMON = \
        objects/vim9compile.o \
        objects/vim9execute.o \
        objects/vim9expr.o \
+       objects/vim9generics.o \
        objects/vim9instr.o \
        objects/vim9script.o \
        objects/vim9type.o \
@@ -1967,6 +1969,7 @@ PRO_AUTO = \
        vim9compile.pro \
        vim9execute.pro \
        vim9expr.pro \
+       vim9generics.pro \
        vim9instr.pro \
        vim9script.pro \
        vim9type.pro \
@@ -3630,6 +3633,9 @@ objects/vim9execute.o: vim9execute.c
 objects/vim9expr.o: vim9expr.c
        $(CCC) -o $@ vim9expr.c
 
+objects/vim9generics.o: vim9generics.c
+       $(CCC) -o $@ vim9generics.c
+
 objects/vim9instr.o: vim9instr.c
        $(CCC) -o $@ vim9instr.c
 
@@ -4357,6 +4363,11 @@ objects/vim9expr.o: vim9expr.c vim.h protodef.h auto/config.h feature.h os_unix.
  proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
  globals.h errors.h vim9.h
+objects/vim9generics.o: vim9generics.c vim.h protodef.h auto/config.h feature.h \
+ os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
+ beval.h proto/gui_beval.pro structs.h regexp.h gui.h \
+ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h alloc.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h vim9.h
 objects/vim9instr.o: vim9instr.c vim.h protodef.h auto/config.h feature.h \
  os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
  beval.h proto/gui_beval.pro structs.h regexp.h gui.h \
index 2a1e11ea67e23e4fa8b1e3e29e0607ed253f0b92..16a0fb3ed02955e28a33efcaa2969f8b0c039792 100644 (file)
@@ -3624,8 +3624,14 @@ EXTERN char e_uninitialized_object_var_reference[]
        INIT(= N_("E1430: Uninitialized object variable '%s' referenced"));
 EXTERN char e_abstract_method_str_direct[]
        INIT(= N_("E1431: Abstract method \"%s\" in class \"%s\" cannot be accessed directly"));
-#endif
-// E1432 - E1499 unused (reserved for Vim9 class support)
+EXTERN char e_generic_method_str_override_with_concrete_method_in_class_str[]
+       INIT(= N_("E1432: Overriding generic method \"%s\" in class \"%s\" with a concrete method"));
+EXTERN char e_concrete_method_str_override_with_generic_method_in_class_str[]
+       INIT(= N_("E1433: Overriding concrete method \"%s\" in class \"%s\" with a generic method"));
+EXTERN char e_generic_method_str_type_arguments_mismatch_in_class_str[]
+       INIT(= N_("E1434: Mismatched number of type variables for generic method  \"%s\" in class \"%s\""));
+#endif
+// E1435 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
        INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
@@ -3746,3 +3752,25 @@ EXTERN char e_failed_to_find_all_diff_anchors[]
 EXTERN char e_cannot_open_a_popup_window_to_a_closing_buffer[]
        INIT(= N_("E1551: Cannot open a popup window to a closing buffer"));
 #endif
+#ifdef FEAT_EVAL
+EXTERN char e_type_var_name_must_start_with_uppercase_letter_str[]
+       INIT(= N_("E1552: Type variable name must start with an uppercase letter: %s"));
+EXTERN char e_missing_comma_in_generic_function_str[]
+       INIT(= N_("E1553: Missing comma after type in generic function: %s"));
+EXTERN char e_missing_closing_angle_bracket_in_generic_function_str[]
+       INIT(= N_("E1554: Missing '>' in generic function: %s"));
+EXTERN char e_empty_type_list_for_generic_function_str[]
+       INIT(= N_("E1555: Empty type list specified for generic function '%s'"));
+EXTERN char e_too_many_types_for_generic_function_str[]
+       INIT(= N_("E1556: Too many types specified for generic function '%s'"));
+EXTERN char e_not_enough_types_for_generic_function_str[]
+       INIT(= N_("E1557: Not enough types specified for generic function '%s'"));
+EXTERN char e_unknown_generic_function_str[]
+       INIT(= N_("E1558: Unknown generic function: %s"));
+EXTERN char e_generic_func_missing_type_args_str[]
+       INIT(= N_("E1559: Type arguments missing for generic function '%s'"));
+EXTERN char e_not_a_generic_function_str[]
+       INIT(= N_("E1560: Not a generic function: %s"));
+EXTERN char e_duplicate_type_var_name_str[]
+       INIT(= N_("E1561: Duplicate type variable name: %s"));
+#endif
index b93bc4201d564fc2f796d5fddffb048eace73d11..604b471d195aeaf9af779e5374b543346e0f63d0 100644 (file)
@@ -1620,12 +1620,12 @@ get_lval_tuple(
  * The method index, method function pointer and method type are returned in
  * "lp".
  */
-    static void
+    static int
 get_lval_oc_method(
     lval_T     *lp,
     class_T    *cl,
     char_u     *key,
-    char_u     *key_end,
+    char_u     **key_end,
     vartype_T  v_type)
 {
     // Look for a method with this name.
@@ -1637,8 +1637,13 @@ get_lval_oc_method(
        ufunc_T *fp;
 
        fp = method_lookup(cl, round == 1 ? VAR_CLASS : VAR_OBJECT,
-                                               key, key_end - key, &m_idx);
+                                               key, *key_end - key, &m_idx);
        lp->ll_oi = m_idx;
+
+       // process generic method (if present)
+       if (fp && (fp = eval_generic_func(fp, key, key_end)) == NULL)
+           return FAIL;
+
        if (fp != NULL)
        {
            lp->ll_ufunc = fp;
@@ -1646,6 +1651,8 @@ get_lval_oc_method(
            break;
        }
     }
+
+    return OK;
 }
 
 /*
@@ -1711,7 +1718,7 @@ get_lval_oc_variable(
 get_lval_class_or_obj(
     lval_T     *lp,
     char_u     *key,
-    char_u     *key_end,
+    char_u     **key_end,
     vartype_T  v_type,
     class_T    *cl_exec,
     int                flags,
@@ -1747,19 +1754,20 @@ get_lval_class_or_obj(
     lp->ll_valtype = NULL;
 
     if (flags & GLV_PREFER_FUNC)
-       get_lval_oc_method(lp, cl, key, key_end, v_type);
+       if (get_lval_oc_method(lp, cl, key, key_end, v_type) == FAIL)
+           return FAIL;
 
     // Look for object/class member variable
     if (lp->ll_valtype == NULL)
     {
-       if (get_lval_oc_variable(lp, cl, key, key_end, v_type, cl_exec, flags)
+       if (get_lval_oc_variable(lp, cl, key, *key_end, v_type, cl_exec, flags)
                                                                == FAIL)
            return FAIL;
     }
 
     if (lp->ll_valtype == NULL)
     {
-       member_not_found_msg(cl, v_type, key, key_end - key);
+       member_not_found_msg(cl, v_type, key, *key_end - key);
        return FAIL;
     }
 
@@ -2039,7 +2047,7 @@ get_lval_subscript(
        }
        else  // v_type == VAR_CLASS || v_type == VAR_OBJECT
        {
-           if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
+           if (get_lval_class_or_obj(lp, key, &p, v_type, cl_exec, flags,
                                                        quiet) == FAIL)
                goto done;
        }
@@ -2213,7 +2221,7 @@ get_lval(
                // parse the type after the name
                lp->ll_type = parse_type(&tp,
                               &SCRIPT_ITEM(current_sctx.sc_sid)->sn_type_list,
-                              !quiet);
+                              NULL, NULL, !quiet);
                if (lp->ll_type == NULL && !quiet)
                    return NULL;
                lp->ll_name_end = tp;
@@ -3179,6 +3187,8 @@ eval_func(
        funcexe.fe_basetv = basetv;
        funcexe.fe_check_type = type;
        funcexe.fe_found_var = found_var;
+       if (evalarg != NULL)
+           funcexe.fe_cctx = evalarg->eval_cctx;
        ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
     }
     vim_free(s);
@@ -4725,7 +4735,7 @@ eval8(
     {
        ++*arg;
        ga_init2(&type_list, sizeof(type_T *), 10);
-       want_type = parse_type(arg, &type_list, TRUE);
+       want_type = parse_type(arg, &type_list, NULL, NULL, TRUE);
        if (want_type == NULL && (evaluate || **arg != '>'))
        {
            clear_type_list(&type_list);
@@ -4973,7 +4983,7 @@ eval9_nested_expr(
 
     if (vim9script)
     {
-       ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
+       ret = get_lambda_tv(arg, rettv, TRUE, evalarg, NULL);
        if (ret == OK && evaluate)
        {
            ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
@@ -5062,7 +5072,8 @@ eval9_var_func_name(
            semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
            ret = FAIL;
        }
-       else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
+       else if ((vim9script ? **arg : *skipwhite(*arg)) == '('
+                               || (vim9script && generic_func_call(arg)))
        {
            // "name(..."  recursive!
            *arg = skipwhite(*arg);
@@ -5079,6 +5090,12 @@ eval9_var_func_name(
                *name_start = s;
                ret = eval_variable(s, len, 0, rettv, NULL,
                                        EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
+
+               // skip the generic function arguments (if present)
+               // they are already processed by eval_variable
+               if (ret == OK && vim9script && **arg == '<'
+                                               && rettv->v_type == VAR_FUNC)
+                   ret = skip_generic_func_type_args(arg);
            }
        }
        else
@@ -5230,7 +5247,7 @@ eval9(
     case '{':  if (vim9script)
                    ret = NOTDONE;
                else
-                   ret = get_lambda_tv(arg, rettv, vim9script, evalarg);
+                   ret = get_lambda_tv(arg, rettv, vim9script, evalarg, NULL);
                if (ret == NOTDONE)
                    ret = eval_dict(arg, rettv, evalarg, FALSE);
                break;
@@ -5438,6 +5455,8 @@ call_func_rettv(
     funcexe.fe_partial = pt;
     funcexe.fe_selfdict = selfdict;
     funcexe.fe_basetv = basetv;
+    if (evalarg != NULL)
+       funcexe.fe_cctx = evalarg->eval_cctx;
     ret = get_func_tv(s, -1, rettv, arg, evalarg, &funcexe);
 
 theend:
@@ -5471,7 +5490,7 @@ eval_lambda(
     if (**arg == '{')
     {
        // ->{lambda}()
-       ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
+       ret = get_lambda_tv(arg, rettv, FALSE, evalarg, NULL);
     }
     else
     {
@@ -5663,6 +5682,15 @@ eval_index(
            ;
        if (keylen == 0)
            return FAIL;
+       if (vim9script && key[keylen] == '<')
+       {
+           // skip generic type arguments
+           char_u      *p = &key[keylen];
+
+           if (skip_generic_func_type_args(&p) == FAIL)
+               return FAIL;
+           keylen = p - key;
+       }
        *arg = key + keylen;
     }
     else
@@ -7374,7 +7402,19 @@ handle_subscript(
            else
            {
                rettv->v_type = VAR_FUNC;
-               rettv->vval.v_string = vim_strnsave(ufunc->uf_name, ufunc->uf_namelen);
+               if (**arg == '<')
+               {
+                   char_u *s = get_generic_func_name(ufunc, arg);
+                   if (s != NULL)
+                       rettv->vval.v_string = s;
+                   else
+                       ret = FAIL;
+               }
+               else
+               {
+                   rettv->vval.v_string =
+                       vim_strnsave(ufunc->uf_name, ufunc->uf_namelen);
+               }
            }
            continue;
        }
index 3c8339ee37e5307e2755cabb1854d165d2b780c9..825df5fbfdb07d26959d89097158fd9a78b51e64 100644 (file)
@@ -3949,6 +3949,17 @@ f_call(typval_T *argvars, typval_T *rettv)
            emsg_funcname(e_unknown_function_str, func);
            return;
        }
+       if (*p == '<')
+       {
+           // generic function
+           char_u *s = append_generic_func_type_args(tofree, STRLEN(tofree),
+                                                                       &p);
+           if (s != NULL)
+           {
+               vim_free(tofree);
+               tofree = s;
+           }
+       }
        func = tofree;
     }
 
@@ -5166,6 +5177,7 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
     partial_T   *arg_pt = NULL;
     char_u     *trans_name = NULL;
     int                is_global = FALSE;
+    char_u     *start_bracket = NULL;
 
     if (in_vim9script()
            && (check_for_string_or_func_arg(argvars, 0) == FAIL
@@ -5203,6 +5215,12 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
        name = s;
        trans_name = save_function_name(&name, &is_global, FALSE,
                   TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL);
+       if (*name == '<')
+       {
+           // generic function
+           start_bracket = name;
+           skip_generic_func_type_args(&name);
+       }
        if (*name != NUL)
            s = NULL;
     }
@@ -5345,6 +5363,9 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
                else if (is_funcref)
                {
                    pt->pt_func = find_func(trans_name, is_global);
+                   if (IS_GENERIC_FUNC(pt->pt_func) && start_bracket != NULL)
+                       pt->pt_func = eval_generic_func(pt->pt_func, s,
+                                                       &start_bracket);
                    func_ptr_ref(pt->pt_func);
                    vim_free(name);
                }
@@ -5367,8 +5388,19 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
        {
            // result is a VAR_FUNC
            rettv->v_type = VAR_FUNC;
-           rettv->vval.v_string = name;
-           func_ref(name);
+           if (start_bracket == NULL)
+           {
+               rettv->vval.v_string = name;
+               func_ref(name);
+           }
+           else
+           {
+               // generic function
+               STRCPY(IObuff, name);
+               STRCAT(IObuff, start_bracket);
+               rettv->vval.v_string = vim_strsave(IObuff);
+               vim_free(name);
+           }
        }
     }
 theend:
index d94d3e860857b7758fa5be1fe0e1081a88a67c50..37e2f373aa6799b56f8d7954b833c742343a6af4 100644 (file)
@@ -3196,6 +3196,20 @@ eval_variable(
            int     has_g_prefix = STRNCMP(name, "g:", 2) == 0;
            ufunc_T *ufunc = find_func(name, FALSE);
 
+           if (ufunc != NULL && cc == '<')
+           {
+               // handle generic function
+               char_u  *argp = name + len;
+               name[len] = cc;
+               ufunc = eval_generic_func(ufunc, name, &argp);
+               name[len] = NUL;
+               if (ufunc == NULL)
+               {
+                   ret = FAIL;
+                   goto done;
+               }
+           }
+
            // In Vim9 script we can get a function reference by using the
            // function name.  For a global non-autoload function "g:" is
            // required.
@@ -3207,9 +3221,22 @@ eval_variable(
                {
                    rettv->v_type = VAR_FUNC;
                    if (has_g_prefix)
+                   {
                        // Keep the "g:", otherwise script-local may be
                        // assumed.
-                       rettv->vval.v_string = vim_strsave(name);
+                       if (cc != '<')
+                           rettv->vval.v_string = vim_strsave(name);
+                       else
+                       {
+                           // append the generic function arguments
+                           char_u      *argp = name + len;
+                           name[len] = cc;
+                           rettv->vval.v_string =
+                               append_generic_func_type_args(name, len,
+                                                                       &argp);
+                           name[len] = NUL;
+                       }
+                   }
                    else
                        rettv->vval.v_string = vim_strnsave(ufunc->uf_name, ufunc->uf_namelen);
                    if (rettv->vval.v_string != NULL)
index 06f8f8b795e2668c7eb732f15d00564b2c87bdd6..d1465202a2a95c17bc4145d61d23f2fc5ceea966 100644 (file)
@@ -3715,6 +3715,16 @@ find_ex_command(
                // "&option" can be followed by "->" or "=", check below
            }
 
+           if (*p == '<' && vim9)
+           {
+               // generic function
+               if (skip_generic_func_type_args(&p) == FAIL)
+               {
+                   eap->cmdidx = CMD_SIZE;
+                   return p;
+               }
+           }
+
            swp = skipwhite(p);
 
            if (
index 88a2081c479937e12e687d3bb65711c4d6d19c2c..ca9cbdd1805508cdf59ebbdfbdfc1dd43352bd14 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-07-20 16:31+0200\n"
+"POT-Creation-Date: 2025-07-21 21:33+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -8553,6 +8553,24 @@ msgid ""
 "E1431: Abstract method \"%s\" in class \"%s\" cannot be accessed directly"
 msgstr ""
 
+#, c-format
+msgid ""
+"E1432: Overriding generic method \"%s\" in class \"%s\" with a concrete "
+"method"
+msgstr ""
+
+#, c-format
+msgid ""
+"E1433: Overriding concrete method \"%s\" in class \"%s\" with a generic "
+"method"
+msgstr ""
+
+#, c-format
+msgid ""
+"E1434: Mismatched number of type variables for generic method  \"%s\" in "
+"class \"%s\""
+msgstr ""
+
 #, c-format
 msgid "E1500: Cannot mix positional and non-positional arguments: %s"
 msgstr ""
@@ -8733,6 +8751,46 @@ msgstr ""
 msgid "E1551: Cannot open a popup window to a closing buffer"
 msgstr ""
 
+#, c-format
+msgid "E1552: Type variable name must start with an uppercase letter: %s"
+msgstr ""
+
+#, c-format
+msgid "E1553: Missing comma after type in generic function: %s"
+msgstr ""
+
+#, c-format
+msgid "E1554: Missing '>' in generic function: %s"
+msgstr ""
+
+#, c-format
+msgid "E1555: Empty type list specified for generic function '%s'"
+msgstr ""
+
+#, c-format
+msgid "E1556: Too many types specified for generic function '%s'"
+msgstr ""
+
+#, c-format
+msgid "E1557: Not enough types specified for generic function '%s'"
+msgstr ""
+
+#, c-format
+msgid "E1558: Unknown generic function: %s"
+msgstr ""
+
+#, c-format
+msgid "E1559: Type arguments missing for generic function '%s'"
+msgstr ""
+
+#, c-format
+msgid "E1560: Not a generic function: %s"
+msgstr ""
+
+#, c-format
+msgid "E1561: Duplicate type variable name: %s"
+msgstr ""
+
 #. type of cmdline window or 0
 #. result of cmdline window or 0
 #. buffer of cmdline window or NULL
index 28cb809d4091611f3a9fe07a108dc8a1439de104..8c345b90f039443f7b149694607a9990e91d8836 100644 (file)
@@ -230,6 +230,7 @@ void mbyte_im_set_active(int active_arg);
 #  include "vim9compile.pro"
 #  include "vim9execute.pro"
 #  include "vim9expr.pro"
+#  include "vim9generics.pro"
 #  include "vim9instr.pro"
 #  include "vim9type.pro"
 # endif
index e91dd3a4918a09581eeee9a4cec4f7fa1c381efa..da5a90f774842e57e538bdb972aa94a2ad9cdc20 100644 (file)
@@ -4,7 +4,7 @@ hashtab_T *func_tbl_get(void);
 char_u *make_ufunc_name_readable(char_u *name, char_u *buf, size_t bufsize);
 string_T get_lambda_name(void);
 char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
-int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
+int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg, cctx_T *cctx);
 char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload, int new_function, int *found_var);
 void emsg_funcname(char *ermsg, char_u *name);
 int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount, int is_builtin);
@@ -46,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
 char_u *alloc_printable_func_name(char_u *fname);
 char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
 void list_functions(regmatch_T *regmatch);
-ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count, cctx_T *cctx);
 void ex_function(exarg_T *eap);
 int get_func_arity(char_u *name, int *required, int *optional, int *varargs);
 ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
@@ -58,7 +58,7 @@ int has_varargs(ufunc_T *ufunc);
 int function_exists(char_u *name, int no_deref);
 char_u *get_expanded_name(char_u *name, int check);
 char_u *get_user_func_name(expand_T *xp, int idx);
-ufunc_T *copy_function(ufunc_T *fp);
+ufunc_T *copy_function(ufunc_T *fp, int extra_namelen);
 void ex_delfunction(exarg_T *eap);
 void func_unref(char_u *name);
 void func_ptr_unref(ufunc_T *fp);
index d28908c40019de6c37141c31ba5fbc2a20c81965..58c79fc0273cb54818ad5fe6a6b2857179e707db 100644 (file)
@@ -3,7 +3,7 @@ int generate_ppconst(cctx_T *cctx, ppconst_T *ppconst);
 void clear_ppconst(ppconst_T *ppconst);
 int compile_member(int is_slice, int *keeping_dict, cctx_T *cctx);
 int compile_load_scriptvar(cctx_T *cctx, char_u *name, char_u *start, char_u **end, imported_T *import);
-int compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int is_expr, int error);
+int compile_load(char_u **arg, size_t namelen, char_u *end_arg, cctx_T *cctx, int is_expr, int error);
 int compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, ca_special_T special_fn);
 char_u *to_name_end(char_u *arg, int use_namespace);
 char_u *to_name_const_end(char_u *arg);
diff --git a/src/proto/vim9generics.pro b/src/proto/vim9generics.pro
new file mode 100644 (file)
index 0000000..2fcae4d
--- /dev/null
@@ -0,0 +1,19 @@
+/* vim9generics.c */
+char_u *generic_func_find_open_bracket(char_u *name);
+int skip_generic_func_type_args(char_u **argp);
+char_u *append_generic_func_type_args(char_u *funcname, size_t namelen, char_u **argp);
+char_u *get_generic_func_name(ufunc_T *fp, char_u **arg);
+char_u *parse_generic_func_type_args(char_u *func_name, size_t namelen, char_u *start, gfargs_tab_T *gfatab, cctx_T *cctx);
+char_u *parse_generic_func_type_params(char_u *func_name, char_u *p, gfargs_tab_T *gfatab, cctx_T *cctx);
+void generic_func_init(ufunc_T *fp, gfargs_tab_T *gfatab);
+void generic_func_args_table_init(gfargs_tab_T *gfatab);
+int generic_func_args_table_size(gfargs_tab_T *gfatab);
+void generic_func_args_table_clear(gfargs_tab_T *gfatab);
+void copy_generic_function(ufunc_T *fp, ufunc_T *new_fp);
+ufunc_T *eval_generic_func(ufunc_T *ufunc, char_u *name, char_u **arg);
+int generic_func_call(char_u **arg);
+ufunc_T *generic_func_get(ufunc_T *fp, gfargs_tab_T *gfatab);
+ufunc_T *find_generic_func(ufunc_T *ufunc, char_u *name, char_u **arg);
+type_T *find_generic_type(char_u *type_name, size_t namelen, ufunc_T *ufunc, cctx_T *cctx);
+void generic_func_clear_items(ufunc_T *fp);
+/* vim: set ft=c : */
index 865a93c1b99fa17977e69faeaae08d3953449644..c32da26ab603059ab4872eb803fcf7c600cda61a 100644 (file)
@@ -1,6 +1,7 @@
 /* vim9type.c */
 type_T *get_type_ptr(garray_T *type_gap);
 type_T *copy_type(type_T *type, garray_T *type_gap);
+type_T *copy_type_deep(type_T *type, garray_T *type_gap);
 void clear_type_list(garray_T *gap);
 void clear_func_type_list(garray_T *gap, type_T **func_type);
 type_T *alloc_type(type_T *type);
@@ -25,7 +26,7 @@ int check_type(type_T *expected, type_T *actual, int give_msg, where_T where);
 int check_type_maybe(type_T *expected, type_T *actual, int give_msg, where_T where);
 int check_argument_types(type_T *type, typval_T *argvars, int argcount, typval_T *base_tv, char_u *name);
 char_u *skip_type(char_u *start, int optional);
-type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
+type_T *parse_type(char_u **arg, garray_T *type_gap, ufunc_T *ufunc, cctx_T *cctx, int give_error);
 int equal_type(type_T *type1, type_T *type2, int flags);
 void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap);
 type_T *get_item_type(type_T *type);
index e3c69ebe3b5fef04063932c4235a1636e596d933..725ec395bdbe717ec11781f0602138af24e1edbd 100644 (file)
@@ -74,6 +74,8 @@ typedef struct dictvar_S      dict_T;
 typedef struct partial_S       partial_T;
 typedef struct blobvar_S       blob_T;
 typedef struct tuplevar_S      tuple_T;
+typedef struct generictype_S   generic_T;
+typedef struct gfargs_tab_S    gfargs_tab_T;
 
 typedef struct window_S                win_T;
 typedef struct wininfo_S       wininfo_T;
@@ -1543,6 +1545,10 @@ typedef struct {
 #define TTFLAG_STATIC      0x10    // one of the static types, e.g. t_any
 #define TTFLAG_CONST       0x20    // cannot be changed
 #define TTFLAG_SUPER       0x40    // object from "super".
+#define TTFLAG_GENERIC     0x80    // generic type
+
+#define IS_GENERIC_TYPE(type)  \
+    ((type->tt_flags & TTFLAG_GENERIC) == TTFLAG_GENERIC)
 
 typedef enum {
     VIM_ACCESS_PRIVATE,        // read/write only inside the class
@@ -1840,6 +1846,25 @@ struct tuplevar_S
     char       tv_lock;        // zero, VAR_LOCKED, VAR_FIXED
 };
 
+/*
+ * Structure to hold a generic type information
+ */
+struct generictype_S
+{
+    type_T     *gt_type;       // generic or concrete type
+    char_u     *gt_name;       // type name
+};
+
+/*
+ * Generic function args table
+ */
+struct gfargs_tab_S
+{
+    garray_T   gfat_args;
+    garray_T   gfat_param_types;
+    garray_T   gfat_arg_types;
+};
+
 typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state);
 typedef void (*cfunc_free_T)(void *state);
 
@@ -1926,6 +1951,13 @@ struct ufunc_S
     void       *uf_cb_state;   // state of uf_cb
 # endif
 
+    // for generic functions
+    int                uf_generic_argcount;// type argument count
+    generic_T  *uf_generic_args;   // generic types
+    type_T     *uf_generic_param_types; // list of allocated generic types
+    garray_T   uf_generic_arg_types; // list of allocated type arguments
+    hashtab_T  uf_generic_functab; // generic function table
+
     garray_T   uf_lines;       // function lines
 
     int                uf_debug_tick;  // when last checked for a breakpoint in this
@@ -1987,6 +2019,7 @@ struct ufunc_S
 #define FC_OBJECT   0x4000     // object method
 #define FC_NEW     0x8000      // constructor
 #define FC_ABSTRACT 0x10000    // abstract method
+#define FC_GENERIC  0x20000    // generic function
 
 // Is "ufunc" an object method?
 #define IS_OBJECT_METHOD(ufunc) ((ufunc->uf_flags & FC_OBJECT) == FC_OBJECT)
@@ -1994,6 +2027,7 @@ struct ufunc_S
 #define IS_CONSTRUCTOR_METHOD(ufunc) ((ufunc->uf_flags & FC_NEW) == FC_NEW)
 // Is "ufunc" an abstract class method?
 #define IS_ABSTRACT_METHOD(ufunc) ((ufunc->uf_flags & FC_ABSTRACT) == FC_ABSTRACT)
+#define IS_GENERIC_FUNC(ufunc) (((ufunc)->uf_flags & FC_GENERIC) == FC_GENERIC)
 
 #define MAX_FUNC_ARGS  20      // maximum number of function arguments
 #define VAR_SHORT_LEN  20      // short variable name length
@@ -2323,6 +2357,7 @@ typedef struct {
     type_T     *fe_check_type; // type from funcref or NULL
     int                fe_found_var;   // if the function is not found then give an
                                // error that a variable is not callable.
+    cctx_T     *fe_cctx;       // when compiling a :def function
 } funcexe_T;
 
 /*
index e61bf2b50e8b59993fa0ad405f606dd19ea3550b..0d4aeb0432fb506008c5f9366e9ed5b1d275683c 100644 (file)
@@ -48,6 +48,7 @@ TEST_VIM9 = \
        test_vim9_expr \
        test_vim9_fails \
        test_vim9_func \
+       test_vim9_generics \
        test_vim9_import \
        test_vim9_python3 \
        test_vim9_script \
@@ -63,6 +64,7 @@ TEST_VIM9_RES = \
        test_vim9_expr.res \
        test_vim9_fails.res \
        test_vim9_func.res \
+       test_vim9_generics.res \
        test_vim9_import.res \
        test_vim9_python3.res \
        test_vim9_script.res \
diff --git a/src/testdir/test_vim9_generics.vim b/src/testdir/test_vim9_generics.vim
new file mode 100644 (file)
index 0000000..2cf93ee
--- /dev/null
@@ -0,0 +1,3548 @@
+" Test Vim9 generic function
+
+import './util/vim9.vim' as v9
+
+" Test for definint a generic function
+def Test_generic_func_definition()
+  var lines =<< trim END
+    vim9script
+    def Fn<A, B>(x: A, y: B): A
+      return x
+    enddef
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    def Fn<t>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1552: Type variable name must start with an uppercase letter: t>()', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T, >()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after  >()', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<,>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <,>(', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1553: Missing comma after type in generic function: T()', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T, ()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after  ()', 2)
+
+  # Use a multi-character generic type name
+  lines =<< trim END
+    vim9script
+    def Fn<MyType1, My_Type2>(a: MyType1, b: My_Type2): My_Type2
+      var x: MyType1
+      var y: My_Type2
+      return b
+    enddef
+    assert_equal([1, 2], Fn<string, list<number>>('a', [1, 2]))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a generic type name starting with a lower case letter
+  lines =<< trim END
+    vim9script
+    def Fn<mytype>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1552: Type variable name must start with an uppercase letter: mytype>()', 2)
+
+  # Use a non-alphanumeric character in the generic type name
+  lines =<< trim END
+    vim9script
+    def Fn<My-type>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1553: Missing comma after type in generic function: My-type>()', 2)
+
+  # Use an existing type name as the generic type name
+  lines =<< trim END
+    vim9script
+    type FooBar = number
+    def Fn<FooBar>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "FooBar"', 3)
+
+  # Use a very long type name
+  lines =<< trim END
+    vim9script
+    def Fn<XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a function name as the generic type name
+  lines =<< trim END
+    vim9script
+    def MyFunc()
+    enddef
+    def Fn<MyFunc>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "MyFunc"', 4)
+enddef
+
+" Test for white space error when defining a generic function
+def Test_generic_func_definition_whitespace_error()
+  var lines =<< trim END
+    vim9script
+    def Fn <A>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '<': <A>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A> ()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '(':  (", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn< A>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after '<': < A>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A >()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'A': A >()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A,>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': ,>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, >()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after  >()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<, A>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <, A>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<,A>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <,A>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn< , A>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after '<': < , A>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A,B>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': ,B>(", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A , B>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'A': A , B>()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B >()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'B': B >()", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<MyType , FooBar>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'MyType': MyType , FooBar>()", 2)
+enddef
+
+" Test for invoking a generic function
+def Test_generic_func_invoke()
+  var lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    Fn<number, number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    Fn<number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<>()
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>()'", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<>
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>'", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<()
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<number>
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: Fn<number>', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<number>(
+  END
+  v9.CheckSourceFailure(lines, 'E116: Invalid arguments for function Fn<number>(', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn <number>()
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: Fn <number>()', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    Fn<number, >()
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >()', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T, X>()
+    enddef
+    Fn<number, abc>()
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: abc', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    Fn<number string>()
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'number': <number", 4)
+
+  # Error when compiling a generic function
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+      xxx
+    enddef
+    Fn<number, string>()
+  END
+  v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 1)
+enddef
+
+" Test for whitespace error when invoking a generic function
+def Test_generic_func_invoke_whitespace_error()
+  var lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn< number>()
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after '<': < number>()", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<number >()
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'number': <number", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<number,>()
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': <number,>()", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    Fn<number, >()
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <number, >()", 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    Fn<number,string>()
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': <number,string>()", 4)
+enddef
+
+def Test_generic_func_typename()
+  var lines =<< trim END
+    vim9script
+
+    def Foo(a: list<string>, b: dict<number>): list<blob>
+      return []
+    enddef
+
+    def Fn<T>(x: T, s: string)
+      assert_equal(s, typename(x))
+    enddef
+
+    Fn<bool>(true, 'bool')
+    Fn<number>(10, 'number')
+    Fn<float>(3.4, 'float')
+    Fn<string>('abc', 'string')
+    Fn<blob>(0z1020, 'blob')
+    Fn<list<list<blob>>>([[0z10, 0z20], [0z30]], 'list<list<blob>>')
+    Fn<tuple<number, string>>((1, 'abc'), 'tuple<number, string>')
+    Fn<dict<string>>({a: 'a', b: 'b'}, 'dict<string>')
+    Fn<job>(test_null_job(), 'job')
+    Fn<channel>(test_null_channel(), 'channel')
+    Fn<func>(function('Foo'), 'func(list<string>, dict<number>): list<blob>')
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_generic_func_single_arg()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): number
+      return len(x)
+    enddef
+
+    assert_equal(3, Fn<list<number>>([1, 2, 3]))
+    assert_equal(2, Fn<dict<number>>({a: 1, b: 2}))
+    assert_equal(1, Fn<blob>(0z10))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a generic type as the type of a function argument
+def Test_generic_func_arg_type()
+  var lines =<< trim END
+    vim9script
+
+    def F1<A>(x: list<A>): list<A>
+      return x
+    enddef
+
+    def F2<B>(y: dict<B>): dict<B>
+      return y
+    enddef
+
+    assert_equal(['a', 'b'], F1<string>(['a', 'b']))
+    assert_equal({a: 0z10, b: 0z20}, F2<blob>({a: 0z10, b: 0z20}))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a tuple type for a generic function argument
+def Test_generic_func_tuple_arg_type()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<T>(x: tuple<T, T>): tuple<T, T>
+      return x
+    enddef
+    assert_equal((1, 2), Fn<number>((1, 2)))
+    assert_equal(('a', 'b'), Fn<string>(('a', 'b')))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<A, B>(x: tuple<A, ...list<B>>): tuple<A, ...list<B>>
+      return x
+    enddef
+    assert_equal(('a', 1, 2), Fn<string, number>(('a', 1, 2)))
+    assert_equal((3, 'a', 'b'), Fn<number, string>((3, 'a', 'b')))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_generic_func_ret_type()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): A
+      return x
+    enddef
+
+    assert_equal([1], Fn<list<number>>([1]))
+    assert_equal({a: 1}, Fn<dict<number>>({a: 1}))
+    assert_equal((1,), Fn<tuple<number>>((1,)))
+    assert_equal(0z10, Fn<blob>(0z10))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using the generic type as the member of the List return value
+  lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): list<A>
+      return [x]
+    enddef
+
+    assert_equal([1], Fn<number>(1))
+    assert_equal(['abc'], Fn<string>('abc'))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using the generic type as the member of the Dict return value
+  lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): dict<A>
+      return {v: x}
+    enddef
+
+    assert_equal({v: 1}, Fn<number>(1))
+    assert_equal({v: 'abc'}, Fn<string>('abc'))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a generic type as the type of the vararg variable
+def Test_generic_func_varargs()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(...x: list<list<A>>): list<list<A>>
+      return x
+    enddef
+
+    assert_equal([[1], [2], [3]], Fn<number>([1], [2], [3]))
+    assert_equal([['a'], ['b'], ['c']], Fn<string>(['a'], ['b'], ['c']))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using func type as a generic function argument type
+def Test_generic_func_type_as_argument()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A, B, C>(Foo: func(A, B): C): string
+        return typename(Foo)
+    enddef
+
+    def F1(a: number, b: string): blob
+        return 0z10
+    enddef
+
+    def F2(a: float, b: blob): string
+        return 'abc'
+    enddef
+
+    assert_equal('func(number, string): blob', Fn<number, string, blob>(F1))
+    assert_equal('func(float, blob): string', Fn<float, blob, string>(F2))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_generic_nested_call()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(n: number, x: A): A
+      if n
+        return x
+      endif
+
+      assert_equal('abc', Fn<string>(1, 'abc'))
+
+      return x
+    enddef
+
+    assert_equal(10, Fn<number>(0, 10))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_generic_failure_in_def_function()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<T>()
+    enddef
+
+    def Foo()
+      Fn<abc>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: abc', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Foo()
+      Fn<abc>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: abc', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<T>()
+    enddef
+
+    def Foo()
+      Fn<number, string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<A, B>()
+    enddef
+
+    def Foo()
+      Fn<number>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<T>()
+    enddef
+
+    def Foo()
+      Fn<>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>()'", 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<A, B>(x: A, y: B)
+    enddef
+
+    def Foo()
+      Fn(10, 'abc')
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn(x: number)
+    enddef
+
+    def Foo()
+      Fn<number>(10)
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<A, B>(x: A, y: B)
+    enddef
+
+    def Foo()
+      Fn<number, string>(10)
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<T>()
+    enddef
+
+    def Foo()
+      Fn<number, >()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >()', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<T, X>()
+    enddef
+
+    def Foo()
+      Fn<number, abc>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: abc', 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Fn<T>()
+    enddef
+
+    def Foo()
+      Fn<number string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1202: No white space allowed after 'number'", 1)
+enddef
+
+" Test for using function() to get a generic funcref
+def Test_get_generic_funcref_using_function()
+  var lines =<< trim END
+    vim9script
+    def Fn<A>(x: A): A
+      return x
+    enddef
+    var Fx = function(Fn<list<number>>)
+    assert_equal([1], Fx([1]))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Get a generic funcref without specifying any type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn)
+    Fx()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+
+  # Get a generic funcref specifying additional type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<number, string>)
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  # Get a generic funcref specifying less type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    var Fx = function(Fn<string>)
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  # Get a generic funcref specifying non-existing type
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<foobar>)
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: foobar', 4)
+
+  # Get a generic funcref specifying an empty type argument list
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<>)
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>)'", 4)
+
+  # Get a generic funcref specifying only the opening bracket after name
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<)
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <', 4)
+
+  # Get a generic funcref specifying only the opening bracket and type
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<number)
+  END
+  v9.CheckSourceFailure(lines, 'E1553: Missing comma after type in generic function:', 4)
+
+  # Get a generic funcref without specifying a type after comma
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function(Fn<number,)
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': <number,)", 4)
+
+  # Get a funcref to a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    var Fx = function(Fn<number>)
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: Fn', 4)
+
+  # Call a generic funcref using a different argument type
+  lines =<< trim END
+    vim9script
+    def Fn<T>(t: T)
+    enddef
+    var Fx = function(Fn<string>)
+    Fx(10)
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 5)
+
+  # Assign a generic funcref return value to a variable of different type
+  lines =<< trim END
+    vim9script
+    def Fn<T>(t: T): T
+      return t
+    enddef
+    var Fx = function(Fn<string>)
+    var x: number = Fx('abc')
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 6)
+
+  # Call a generic funcref specifying types
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    var Fx = function(Fn<string>)
+    Fx<string>()
+  END
+  v9.CheckSourceFailure(lines, 'E15: Invalid expression: "Fx<string>()"', 5)
+enddef
+
+def Test_generic_funcref_string()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): A
+      return x
+    enddef
+
+    var Fx = function('Fn<list<number>>')
+    assert_equal([1], Fx([1]))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Get a generic funcref without specifying any type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn')
+    Fx()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+
+  # Get a generic funcref specifying additional type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<number, string>')
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  # Get a generic funcref specifying less type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    var Fx = function('Fn<string>')
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  # Get a generic funcref specifying non-existing type
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<foobar>')
+  END
+  v9.CheckSourceFailure(lines, 'E1010: Type not recognized: foobar', 4)
+
+  # Get a generic funcref specifying an empty type argument list
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<>')
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 4)
+
+  # Get a generic funcref specifying only the opening bracket after name
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<')
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 4)
+
+  # Get a generic funcref specifying only the opening bracket and type
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<number')
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number", 4)
+
+  # Get a generic funcref without specifying a type after comma
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn<number,')
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number,", 4)
+
+  # Get a funcref to a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    var Fx = function('Fn<number>')
+    Fx()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function:', 5)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn')
+    Fx<string>()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn')
+    Fx<>()
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>()'", 5)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn')
+    Fx<number, string>()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    var Fx = function('Fn')
+    Fx<string>()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    var Fx = function('Fn')
+    Fx()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 5)
+enddef
+
+" Test for calling a generic funcref from another function
+def Test_generic_funcref_string_from_another_function()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A>(x: A): A
+      return x
+    enddef
+
+    def Foo()
+      var Fx = function('Fn<list<number>>')
+      assert_equal([1], Fx([1]))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Get a generic funcref without specifying any type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 2)
+
+  # Get a generic funcref specifying additional type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<number, string>')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 2)
+
+  # Get a generic funcref specifying less type arguments
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<string>')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 2)
+
+  # Get a generic funcref specifying an empty type argument list
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<>')
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 1)
+
+  # Get a generic funcref specifying only the opening bracket after name
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<')
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 1)
+
+  # Get a generic funcref specifying only the opening bracket and type
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<number')
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number", 1)
+
+  # Get a generic funcref without specifying a type after comma
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn<number,')
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number,", 1)
+
+  # Get a funcref to a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    def Foo()
+      var Fx = function('Fn<number>')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function:', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>(x: A): A
+      return x
+    enddef
+    def Foo()
+      var Fx = function('Fn')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn')
+      Fx<>()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn')
+      Fx<number>()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      var Fx = function('Fn')
+      Fx()
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 2)
+enddef
+
+def Test_generic_obj_method()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<X>(t: X): X
+        var n: X = t
+        return n
+      enddef
+    endclass
+
+    var a = A.new()
+    assert_equal(['a', 'b'], a.Fn<list<string>>(['a', 'b']))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<>()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function 'Fn'", 4)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<,>()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <,>()", 4)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+    var a = A.new()
+    a.Fn<>()
+  END
+  v9.CheckSourceFailureList(lines, ["E1555: Empty type list specified for generic function '<>()'"])
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+    var a = A.new()
+    a.Fn<number, string>()
+  END
+  v9.CheckSourceFailure(lines, "E1556: Too many types specified for generic function 'Fn'", 8)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<X, Y>()
+      enddef
+    endclass
+    var a = A.new()
+    a.Fn<string>()
+  END
+  v9.CheckSourceFailure(lines, "E1557: Not enough types specified for generic function 'Fn'", 8)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+    var a = A.new()
+    a.Fn()
+  END
+  v9.CheckSourceFailure(lines, "E1559: Type arguments missing for generic function", 8)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn()
+      enddef
+    endclass
+    var a = A.new()
+    a.Fn<number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: Fn', 8)
+enddef
+
+def Test_generic_obj_method_call_from_another_method()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<X>(t: X): X
+        var n: X = t
+        return n
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      assert_equal(['a', 'b'], a.Fn<list<string>>(['a', 'b']))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn<>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailureList(lines, ["E1555: Empty type list specified for generic function '<>()'"])
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn<number, string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1556: Too many types specified for generic function 'Fn'", 2)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<X, Y>()
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn<string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1557: Not enough types specified for generic function 'Fn'", 2)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1559: Type arguments missing for generic function", 2)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn()
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn<number>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: Fn', 2)
+
+  # Try calling a non-existing generic object method
+  lines =<< trim END
+    vim9script
+
+    class A
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Bar<number, string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "Bar" not found in class "A"', 2)
+
+  # Error in compiling generic object method arguments
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T, X>(x: number, y: number)
+      enddef
+    endclass
+
+    def Foo()
+      var a = A.new()
+      a.Fn<number, string>(10,)
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': ,)", 2)
+
+  # Try calling a super abstract method from a child class
+  lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def F1()
+    endclass
+
+    class B extends A
+      def F1()
+      enddef
+      def Foo()
+        super.F1<number, string>()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1431: Abstract method "F1" in class "A" cannot be accessed directly', 1)
+
+  # Try calling a protected method in a class
+  lines =<< trim END
+    vim9script
+
+    class A
+      def _Foo()
+      enddef
+    endclass
+
+    def Bar()
+      var a = A.new()
+      a._Foo<number, string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo<number, string>()', 2)
+enddef
+
+def Test_generic_class_method()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<X>(t: X): X
+        var n: X = t
+        return n
+      enddef
+    endclass
+
+    assert_equal(['a', 'b'], A.Fn<list<string>>(['a', 'b']))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<>()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function 'Fn'", 4)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<,>()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <,>()", 4)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+    A.Fn<>()
+  END
+  v9.CheckSourceFailureList(lines, ["E1555: Empty type list specified for generic function '<>()'"])
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+    A.Fn<number, string>()
+  END
+  v9.CheckSourceFailure(lines, "E1556: Too many types specified for generic function 'Fn'", 7)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    A.Fn<string>()
+  END
+  v9.CheckSourceFailure(lines, "E1557: Not enough types specified for generic function 'Fn'", 7)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+    A.Fn()
+  END
+  v9.CheckSourceFailure(lines, "E1559: Type arguments missing for generic function 'Fn'", 7)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn()
+      enddef
+    endclass
+    A.Fn<number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: Fn', 7)
+enddef
+
+def Test_generic_class_method_call_from_another_method()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<X>(t: X): X
+        var n: X = t
+        return n
+      enddef
+    endclass
+
+    def Foo()
+      assert_equal(['a', 'b'], A.Fn<list<string>>(['a', 'b']))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      A.Fn<>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailureList(lines, ["E1555: Empty type list specified for generic function '<>()'"])
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      A.Fn<number, string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1556: Too many types specified for generic function 'Fn'", 1)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<X, Y>()
+      enddef
+    endclass
+
+    def Foo()
+      A.Fn<string>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1557: Not enough types specified for generic function 'Fn'", 1)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn<T>()
+      enddef
+    endclass
+
+    def Foo()
+      A.Fn()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1559: Type arguments missing for generic function 'Fn'", 1)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      static def Fn()
+      enddef
+    endclass
+
+    def Foo()
+      A.Fn<number>()
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: Fn', 1)
+enddef
+
+" Test for using a generic funcref from another method
+def Test_generic_funcref_use_from_def_method()
+  var lines =<< trim END
+    vim9script
+
+    def Foo<T>(t: T): T
+      return t
+    enddef
+
+    def Fx()
+      var Fn = Foo<list<string>>
+      var x: list<string> = Fn(['abc', 'b'])
+      assert_equal(['abc', 'b'], x)
+    enddef
+    Fx()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Assigning a generic function without specifying any type arguments
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+    enddef
+
+    def Fx()
+      var Fn = Foo
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 1)
+
+  # Assigning a generic function specifying additional type arguments
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<number, string>
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 1)
+
+  # Assigning a generic function specifying less type arguments
+  lines =<< trim END
+    vim9script
+
+    def Foo<X, Y>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<string>
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 1)
+
+  # Assigning a generic function specifying an empty type argument list
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<>
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 1)
+
+  # Assigning a generic function specifying only the opening bracket
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 1)
+
+  # Assigning a generic function without specifying the closing bracket
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<number
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number", 1)
+
+  # Assigning a generic function without specifying a type after comma
+  lines =<< trim END
+    vim9script
+
+    def Foo<X, Y>()
+    enddef
+
+    def Fx()
+      var Fn = Foo<number,
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number,", 1)
+
+  # Create a funcref to a regular function as a generic function
+  lines =<< trim END
+    vim9script
+
+    def Foo()
+    enddef
+
+    def Fx()
+      var Fn = Foo<number>
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 1)
+
+  # Call a generic funcref using a different argument type
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>(t: T)
+    enddef
+
+    def Fx()
+      var Fn = Foo<string>
+      Fn(10)
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 2)
+
+  # Assign a generic funcref return value to a variable of different type
+  lines =<< trim END
+    vim9script
+
+    def Foo<T>(t: T): T
+      return t
+    enddef
+
+    def Fx()
+      var Fn = Foo<string>
+      var x: number = Fn('abc')
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 2)
+enddef
+
+def Test_generic_vim9_lambda()
+  var lines =<< trim END
+    vim9script
+
+    def Fn<A, B, C>()
+      var Lambda = (x: A, y: B): C => x + y
+      assert_equal(30, Lambda(10, 20))
+    enddef
+
+    Fn<number, number, number>()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a generic type in a nested def function
+def Test_generic_nested_def()
+  var lines =<< trim END
+    vim9script
+    def F1<A, B, C>()
+      def F2(x: A, y: B): C
+        return x + y
+      enddef
+      assert_equal(30, F2(10, 20))
+    enddef
+    F1<number, number, number>()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Lambda function in a nested def function
+  lines =<< trim END
+    vim9script
+    def F1<A, B, C>()
+      def F2(): func
+        var Lambda = (x: A, y: B): C => x + y
+        return Lambda
+      enddef
+      var Fx = F2()
+      assert_equal(60, Fx(20, 40))
+    enddef
+    F1<number, number, number>()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for type substitution in a generic function call.  Only the generic types
+" should be substituted.  Other "any" types should be ignored.
+def Test_generic_type_substitution()
+  var lines =<< trim END
+    vim9script
+    def Fn<T>(a: any, b: T): any
+      return a
+    enddef
+    assert_equal('abc', Fn<number>('abc', 20))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for a global generic function g:MyFunc<T>
+def Test_generic_global_function()
+  var lines =<< trim END
+    vim9script
+    def g:Fn1<T>(a: T): T
+      return a
+    enddef
+    assert_equal('abc', g:Fn1<string>('abc'))
+
+    def Foo()
+      assert_equal(['a', 'b'], g:Fn1<list<string>>(['a', 'b']))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling a nested generic function
+def Test_generic_function_call_nested()
+  var lines =<< trim END
+    vim9script
+
+    def Id_<T>(o: T): T
+      return o
+    enddef
+
+    class Test
+      def Id<U>(o: U): U
+        return Id_<U>(o)
+      enddef
+    endclass
+
+    assert_equal(".", Id_<string>("."))
+    assert_equal(0, Id_<number>(0))
+    assert_equal(false, Id_<bool>(false))
+    assert_equal(false, Id_<bool>(false))
+    assert_equal(0, Id_<number>(0))
+    assert_equal(".", Id_<string>("."))
+    assert_equal([], Test.new().Id<any>([]))
+    assert_equal(".", Test.new().Id<string>("."))
+    assert_equal(0, Test.new().Id<number>(0))
+    assert_equal(false, Test.new().Id<bool>(false))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for an imported generic function foo#MyFunc<T>
+def Test_generic_import()
+  var lines =<< trim END
+    vim9script
+    export def Fn<A, B>(a: A, b: B): B
+      return b
+    enddef
+    export def Foobar()
+    enddef
+  END
+  writefile(lines, 'Ximport_generic.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+    assert_equal(20, Foo.Fn<string, number>('abc', 20))
+    def MyFunc()
+      assert_equal('xyz', Foo.Fn<number, string>(30, 'xyz'))
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Fn<string>('abc', 20)
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Fn<string>('abc', 20)
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 1)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Fn<string, number>(10, 20)
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Fn<string, number>(10, 20)
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 1)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Fn(10, 20)
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Fn(10, 20)
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 1)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Fn<string, ('abc', 20)
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <string,", 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Fn<string, ('abc', 20)
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <string, ", 1)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Foobar<string>()
+  END
+  v9.CheckSourceFailure(lines, "E1560: Not a generic function", 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Foobar<string>()
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, "E1560: Not a generic function: Foo", 1)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    Foo.Fx<string, number>('abc', 20)
+  END
+  v9.CheckSourceFailure(lines, 'E1048: Item not found in script: Fx', 4)
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic.vim" as Foo
+
+    def MyFunc()
+      Foo.Fx<string, number>('abc', 20)
+    enddef
+    MyFunc()
+  END
+  v9.CheckSourceFailure(lines, 'E1048: Item not found in script: Fx', 1)
+
+  # Source the script twice
+  lines =<< trim END
+    vim9script
+    export def Fn<A>(a: list<A>)
+    enddef
+  END
+  writefile(lines, 'Ximport_generic_2.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import "./Ximport_generic_2.vim" as Foo
+
+    Foo.Fn<list<dict<any>>>([[{}]])
+  END
+  :new
+  setline(1, lines)
+  :source
+  :source
+  :bw!
+enddef
+
+" Test for disassembling a generic function
+def Test_generic_function_disassemble()
+  var lines =<< trim END
+    vim9script
+    def Fn<A, B>(): A
+      var x: A
+      var y: B
+      [x, y] = g:values
+      return x
+    enddef
+    g:instr = execute('disassemble Fn<list<string>, dict<number>>')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn<list<string>, dict<number>>\_s*' ..
+    'var x: A\_s*' ..
+    '0 NEWLIST size 0\_s*' ..
+    '1 SETTYPE list<string>\_s*' ..
+    '2 STORE $0\_s*' ..
+    'var y: B\_s*' ..
+    '3 NEWDICT size 0\_s*' ..
+    '4 SETTYPE dict<number>\_s*' ..
+    '5 STORE $1\_s*' ..
+    '\[x, y\] = g:values\_s*' ..
+    '6 LOADG g:values\_s*\_s*' ..
+    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '8 CHECKLEN 2\_s*' ..
+    '9 ITEM 0\_s*' ..
+    '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
+    '11 SETTYPE list<string>\_s*' ..
+    '12 STORE $0\_s*' ..
+    '13 ITEM 1\_s*' ..
+    '14 CHECKTYPE dict<number> stack\[-1\] var 2\_s*' ..
+    '15 SETTYPE dict<number>\_s*' ..
+    '16 STORE $1\_s*' ..
+    '17 DROP\_s*' ..
+    'return x\_s*' ..
+    '18 LOAD $0\_s*' ..
+    '19 RETURN', g:instr)
+  unlet g:instr
+
+  lines =<< trim END
+    vim9script
+    disassemble Fn<number, dict<number>
+  END
+  v9.CheckScriptFailure(lines, "E1554: Missing '>' in generic function: <number, dict<number>", 2)
+
+  lines =<< trim END
+    vim9script
+    disassemble Fn<number, dict<number>>
+  END
+  v9.CheckScriptFailure(lines, 'E1061: Cannot find function Fn<number, dict<number>>', 2)
+
+  lines =<< trim END
+    vim9script
+    disassemble Fn<number,
+  END
+  v9.CheckScriptFailure(lines, "E1554: Missing '>' in generic function: <number,", 2)
+
+  lines =<< trim END
+    vim9script
+    disassemble Fn<
+  END
+  v9.CheckScriptFailure(lines, "E1554: Missing '>' in generic function: <", 2)
+
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    disassemble Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1560: Not a generic function:', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    disassemble Fn<number, string>
+  END
+  v9.CheckScriptFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+    enddef
+    disassemble Fn
+  END
+  v9.CheckScriptFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    disassemble Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    disassemble Fn<>
+  END
+  v9.CheckScriptFailure(lines, "E1555: Empty type list specified for generic function '<>'", 4)
+enddef
+
+" Test for disassembling a generic function calling another generic function
+def Test_nested_generic_func_call_disassemble()
+  var lines =<< trim END
+    vim9script
+    def Fn1<T>(o: T): T
+      return o
+    enddef
+
+    def Fn2<U>(o: U): U
+      return Fn1<U>(o)
+    enddef
+
+    g:instr1 = execute('disassemble Fn2<string>')
+    g:instr2 = execute('disassemble Fn2<number>')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn2<string>\_s*' ..
+    'return Fn1<U>(o)\_s*' ..
+    '0 LOAD arg\[-1\]\_s*' ..
+    '1 DCALL <SNR>\d\+_Fn1<string>(argc 1)\_s*' ..
+    '2 RETURN\_s*', g:instr1)
+  assert_match('<SNR>\d\+_Fn2<number>\_s*' ..
+    'return Fn1<U>(o)\_s*' ..
+    '0 LOAD arg\[-1\]\_s*' ..
+    '1 DCALL <SNR>\d\+_Fn1<number>(argc 1)\_s*' ..
+    '2 RETURN\_s*', g:instr2)
+
+  unlet g:instr1
+  unlet g:instr2
+enddef
+
+" Test for disassembling a generic object method
+def Test_generic_disassemble_generic_obj_method()
+  var lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<A, B>()
+        var x: A
+        var y: B
+        [x, y] = g:values
+      enddef
+    endclass
+    g:instr = execute('disassemble Foo.Fn<list<string>, dict<number>>')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('Fn<list<string>, dict<number>>\_s*' ..
+    'var x: A\_s*' ..
+    '0 NEWLIST size 0\_s*' ..
+    '1 SETTYPE list<string>\_s*' ..
+    '2 STORE $1\_s*' ..
+    'var y: B\_s*' ..
+    '3 NEWDICT size 0\_s*' ..
+    '4 SETTYPE dict<number>\_s*' ..
+    '5 STORE $2\_s*' ..
+    '\[x, y\] = g:values\_s*' ..
+    '6 LOADG g:values\_s*' ..
+    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '8 CHECKLEN 2\_s*' ..
+    '9 ITEM 0\_s*' ..
+    '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
+    '11 SETTYPE list<string>\_s*' ..
+    '12 STORE $1\_s*' ..
+    '13 ITEM 1\_s*' ..
+    '14 CHECKTYPE dict<number> stack\[-1\] var 2\_s*' ..
+    '15 SETTYPE dict<number>\_s*' ..
+    '16 STORE $2\_s*' ..
+    '17 DROP\_s*' ..
+    '18 RETURN void', g:instr)
+  unlet g:instr
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number, dict<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1553: Missing comma after type in generic function: <number, dict<number>', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+    endclass
+    disassemble Foo.Fn<number, dict<number>>
+  END
+  v9.CheckScriptFailure(lines, 'E1337: Class variable "Fn" not found in class "Foo"', 4)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number,
+  END
+  v9.CheckScriptFailure(lines, "E1069: White space required after ','", 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<
+  END
+  v9.CheckScriptFailure(lines, 'E475: Invalid argument: Foo.Fn<', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn()
+      enddef
+    endclass
+    disassemble Foo.Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1560: Not a generic function:', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number, string>
+  END
+  v9.CheckScriptFailure(lines, 'E1556: Too many types specified for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X>()
+      enddef
+    endclass
+    disassemble Foo.Fn
+  END
+  v9.CheckScriptFailure(lines, 'E1559: Type arguments missing for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1557: Not enough types specified for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<>
+  END
+  v9.CheckScriptFailure(lines, "E1555: Empty type list specified for generic function 'Fn'", 6)
+enddef
+
+" Test for disassembling a generic class method
+def Test_generic_disassemble_generic_class_method()
+  var lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<A, B>()
+        var x: A
+        var y: B
+        [x, y] = g:values
+      enddef
+    endclass
+    g:instr = execute('disassemble Foo.Fn<list<string>, dict<number>>')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('Fn<list<string>, dict<number>>\_s*' ..
+    'var x: A\_s*' ..
+    '0 NEWLIST size 0\_s*' ..
+    '1 SETTYPE list<string>\_s*' ..
+    '2 STORE $0\_s*' ..
+    'var y: B\_s*' ..
+    '3 NEWDICT size 0\_s*' ..
+    '4 SETTYPE dict<number>\_s*' ..
+    '5 STORE $1\_s*' ..
+    '\[x, y\] = g:values\_s*' ..
+    '6 LOADG g:values\_s*' ..
+    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '8 CHECKLEN 2\_s*' ..
+    '9 ITEM 0\_s*' ..
+    '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
+    '11 SETTYPE list<string>\_s*' ..
+    '12 STORE $0\_s*' ..
+    '13 ITEM 1\_s*' ..
+    '14 CHECKTYPE dict<number> stack\[-1\] var 2\_s*' ..
+    '15 SETTYPE dict<number>\_s*' ..
+    '16 STORE $1\_s*' ..
+    '17 DROP\_s*' ..
+    '18 RETURN void', g:instr)
+  unlet g:instr
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number, dict<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1553: Missing comma after type in generic function: <number, dict<number>', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number,
+  END
+  v9.CheckScriptFailure(lines, "E1069: White space required after ','", 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<
+  END
+  v9.CheckScriptFailure(lines, 'E475: Invalid argument: Foo.Fn<', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn()
+      enddef
+    endclass
+    disassemble Foo.Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1560: Not a generic function:', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number, string>
+  END
+  v9.CheckScriptFailure(lines, 'E1556: Too many types specified for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X>()
+      enddef
+    endclass
+    disassemble Foo.Fn
+  END
+  v9.CheckScriptFailure(lines, 'E1559: Type arguments missing for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<number>
+  END
+  v9.CheckScriptFailure(lines, 'E1557: Not enough types specified for generic function', 6)
+
+  lines =<< trim END
+    vim9script
+    class Foo
+      static def Fn<X, Y>()
+      enddef
+    endclass
+    disassemble Foo.Fn<>
+  END
+  v9.CheckScriptFailure(lines, "E1555: Empty type list specified for generic function 'Fn'", 6)
+enddef
+
+" Test for disassembling a generic function using a Funcref variable
+def Test_generic_disassemble_variable()
+  var lines =<< trim END
+    vim9script
+
+    def Foo<T>()
+      echomsg "Foo"
+    enddef
+
+    var Fn = Foo<string>
+    disassemble Fn
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Foo<string>\_s*' ..
+    'echomsg "Foo"\_s*' ..
+    '0 PUSHS "Foo"\_s*' ..
+    '1 ECHOMSG 1\_s*' ..
+    '2 RETURN void\_s*', g:instr)
+  unlet g:instr
+enddef
+
+def Test_generic_duplicate_names()
+  var lines =<< trim END
+    vim9script
+    def Fn<A, B, A>()
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1561: Duplicate type variable name: A', 2)
+
+  lines =<< trim END
+    vim9script
+    class A
+    endclass
+    def Fn<A, B>()
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "A"', 4)
+
+  lines =<< trim END
+    vim9script
+    type A = number
+    def Fn<A, B>()
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "A"', 3)
+
+  lines =<< trim END
+    vim9script
+    var B = 'abc'
+    def Fn<A, B>()
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "B"', 3)
+
+  lines =<< trim END
+    vim9script
+    def Fn1<A, B>()
+    enddef
+    def Fn2<A, B>()
+    enddef
+    defcompile
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    def I<A>(x: A): A
+      return x
+    enddef
+
+    def Id<I>(x: I): I
+      return x
+    enddef
+  END
+  v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "I"', 7)
+enddef
+
+" Test for nested generic functions
+def Test_generic_nested_functions()
+  var lines =<< trim END
+    vim9script
+    def Fn<T>(t: T): T
+      def Fx<A>(a: A): A
+        return a
+      enddef
+      return Fx<number>(t)
+    enddef
+    assert_equal(100, Fn<number>(100))
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # Use the generic type from the outer generic function
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>(b: B): B
+      def Fx<T>(t: T): T
+        return t
+      enddef
+      return Fx<B>(b)
+    enddef
+    assert_equal(100, Fn<number, number>(100))
+    assert_equal('abc', Fn<number, string>('abc'))
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # duplicate definition
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fn<A>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1073: Name already defined: Fn', 1)
+
+  # overlaps with a script-local function
+  lines =<< trim END
+    vim9script
+    def Fx()
+    enddef
+    def Fn<T>()
+      def Fx<A>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1073: Name already defined: Fx', 1)
+
+  # overlaps with another nested function
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<A>()
+      enddef
+      def Fx<B>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1073: Name already defined: Fx', 3)
+
+  # Empty list of types
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1555: Empty type list specified for generic function', 3)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<,>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1008: Missing <type> after <', 3)
+
+  # missing closing bracket in the inner generic function
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<A()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, "E1553: Missing comma after type in generic function: <A()", 3)
+
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<a>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1552: Type variable name must start with an uppercase letter: a>()', 1)
+
+  # duplicate generic type
+  lines =<< trim END
+    vim9script
+    def Fn<T>()
+      def Fx<T>()
+      enddef
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1561: Duplicate type variable name: T', 1)
+enddef
+
+" Test for using a generic function in call() as a string
+def Test_generic_function_use_in_call_function_as_string()
+  var lines =<< trim END
+    vim9script
+    def Fn<A>(a: A): A
+      return a
+    enddef
+    assert_equal(['a', 'b'], call("Fn<list<string>>", [['a', 'b']]))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for passing more types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call("Fn<number, string>", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  # Test for passing less types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call("Fn<string>", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  # Test for passing empty types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call("Fn<>", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 4)
+
+  # Test for passing no types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call("Fn", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  # Test for missing types and closing bracket
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call("Fn<", [])
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call("Fn<number", [])
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call("Fn<number,", [])
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number,", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call("Fn<number,>", [])
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ','", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call("Fn<number, >", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >', 4)
+
+  # Test for calling a non-existing generic function
+  lines =<< trim END
+    vim9script
+    call("FooBar<number>", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1558: Unknown generic function', 2)
+
+  # Test for calling a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    call("Fn<number>", [])
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 4)
+enddef
+
+" Test for using a generic function in call() as a string in a method
+def Test_generic_use_in_call_func_as_string_in_method()
+  var lines =<< trim END
+    vim9script
+    def Fn<A>(a: A): A
+      return a
+    enddef
+    def Foo()
+      assert_equal(['a', 'b'], call("Fn<list<string>>", [['a', 'b']]))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for passing more types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      call("Fn<number, string>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 1)
+
+  # Test for passing less types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    def Foo()
+      call("Fn<string>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 1)
+
+  # Test for passing empty types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      call("Fn<>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 1)
+
+  # Test for passing no types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      call("Fn", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 1)
+
+  # Test for missing types and closing bracket
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      call("Fn<", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <", 1)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    def Foo()
+      call("Fn<number", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number", 1)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    def Foo()
+      call("Fn<number,", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1554: Missing '>' in generic function: <number,", 1)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    def Foo()
+      call("Fn<number,>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ','", 1)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    def Foo()
+      call("Fn<number, >", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >', 1)
+
+  # Test for calling a non-existing generic function
+  lines =<< trim END
+    vim9script
+    def Foo()
+      call("FooBar<number>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1558: Unknown generic function', 1)
+
+  # Test for calling a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    def Foo()
+      call("Fn<number>", [])
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 1)
+enddef
+
+" Test for using a generic function in call() as a funcref
+def Test_generic_function_use_in_call_function_as_funcref()
+  var lines =<< trim END
+    vim9script
+    def Fn<A>(a: A): A
+      return a
+    enddef
+    assert_equal({a: 'xyz'}, call(Fn<dict<string>>, [{a: 'xyz'}]))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for passing more types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call(Fn<number, string>, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  # Test for passing less types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call(Fn<string>, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  # Test for passing empty types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call(Fn<>, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 4)
+
+  # Test for passing no types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call(Fn, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  # Test for missing types and closing bracket
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call(Fn<, [])
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call(Fn<number, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number,', 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call(Fn<number,, [])
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': <number,, [])", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call(Fn<number,>, [])
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ','", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call(Fn<number, >, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >', 4)
+
+  # Test for calling a non-existing generic function
+  lines =<< trim END
+    vim9script
+    call(FooBar<number>, [])
+  END
+  v9.CheckSourceFailure(lines, 'E121: Undefined variable: FooBar', 2)
+
+  # Test for calling a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    call(Fn<number>, [])
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 4)
+enddef
+
+" Test for using a generic function with the :call command
+def Test_generic_function_use_call_cmd()
+  var lines =<< trim END
+    vim9script
+    var funcArgs = {}
+    def Fn<A>(a: A)
+      funcArgs = a
+    enddef
+    call Fn<dict<string>>({a: 'xyz'})
+    assert_equal({a: 'xyz'}, funcArgs)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for passing extra types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call Fn<number, string>()
+  END
+  v9.CheckSourceFailure(lines, 'E1556: Too many types specified for generic function', 4)
+
+  # Test for passing less types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call Fn<string>()
+  END
+  v9.CheckSourceFailure(lines, 'E1557: Not enough types specified for generic function', 4)
+
+  # Test for passing empty types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call Fn<>()
+  END
+  v9.CheckSourceFailure(lines, 'E1555: Empty type list specified for generic function', 4)
+
+  # Test for passing no types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1559: Type arguments missing for generic function', 4)
+
+  # Test for missing types and closing bracket
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call Fn<()
+  END
+  v9.CheckSourceFailure(lines, "E1008: Missing <type> after <", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A>()
+    enddef
+    call Fn<number, ()
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number,', 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call Fn<number,,()
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': <number,,()", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call Fn<number,>()
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ','", 4)
+
+  # Test for missing types
+  lines =<< trim END
+    vim9script
+    def Fn<A, B>()
+    enddef
+    call Fn<number, >()
+  END
+  v9.CheckSourceFailure(lines, 'E1008: Missing <type> after <number, >', 4)
+
+  # Test for calling a non-existing generic function
+  lines =<< trim END
+    vim9script
+    call FooBar<number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1558: Unknown generic function', 2)
+
+  # Test for calling a regular function as a generic function
+  lines =<< trim END
+    vim9script
+    def Fn()
+    enddef
+    call Fn<number>()
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function', 4)
+enddef
+
+def Test_generic_function_with_sort()
+  var lines =<< trim END
+    vim9script
+
+    def g:CompareByLength<T>(a: T, b: T): number
+      return string(a)->strlen() - string(b)->strlen()
+    enddef
+
+    var l = ['aaaa', 'aaa', 'a', 'aa']
+                      ->sort((a, b) => g:CompareByLength<string>(a, b))
+    assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+    l = ['aaaa', 'aaa', 'a', 'aa']
+                      ->sort(g:CompareByLength<string>)
+    assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+    l = ['aaaa', 'aaa', 'a', 'aa']
+                      ->sort(function(g:CompareByLength<string>))
+    assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+    l = ['aaaa', 'aaa', 'a', 'aa']
+                      ->sort(funcref('g:CompareByLength<string>'))
+    assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+    l = ['aaaa', 'aaa', 'a', 'aa']
+                      ->sort(function('g:CompareByLength<string>'))
+    assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    def Foo()
+      var l = ['aaaa', 'aaa', 'a', 'aa']
+                        ->sort((a, b) => g:CompareByLength<string>(a, b))
+      assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+      l = ['aaaa', 'aaa', 'a', 'aa']
+                        ->sort(g:CompareByLength<string>)
+      assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+      l = ['aaaa', 'aaa', 'a', 'aa']
+                        ->sort(function(g:CompareByLength<string>))
+      assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+      l = ['aaaa', 'aaa', 'a', 'aa']
+                        ->sort(funcref('g:CompareByLength<string>'))
+      assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+
+      l = ['aaaa', 'aaa', 'a', 'aa']
+                        ->sort(function('g:CompareByLength<string>'))
+      assert_equal(['a', 'aa', 'aaa', 'aaaa'], l)
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using a generic type in the return value of a function.
+def Test_generic_function_func_ret_val()
+  var lines =<< trim END
+    vim9script
+
+    def Id<U>(): func(U): U
+      return (X: U) => X
+    enddef
+
+    assert_equal(123, Id<number>()(123))
+    assert_equal(321, Id<func>()(Id<number>())(321))
+    const F: func(number): number = Id<number>()
+    var G: func(number): number
+    G = Id<func>()(F)
+    assert_equal(true, typename(F) == typename(G))
+    assert_equal(456, G(456))
+
+    G = Id<func(number): number>()(F)
+    assert_equal(456, G(456))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    def Id<U>(): func(U): U
+      return (X: U) => X
+    enddef
+
+    def Foo()
+      assert_equal(123, Id<number>()(123))
+      assert_equal(321, Id<func>()(Id<number>())(321))
+      const F: func(number): number = Id<number>()
+      var G: func(number): number
+      G = Id<func>()(F)
+      assert_equal(true, typename(F) == typename(G))
+      assert_equal(456, G(456))
+
+      G = Id<func(number): number>()(F)
+      assert_equal(456, G(456))
+    enddef
+    Foo()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for recursively calling a generic function
+def Test_generic_function_recursive_call()
+  var lines =<< trim END
+    vim9script
+
+    def Y0_<U>(): func(func(U): U): U
+      return (F: func(U): U) => F(Y0_<U>()(F))
+    enddef
+
+    const Partial_: func(func(any): any): any = Y0_<any>()
+    Partial_((x: any) => x)
+  END
+  v9.CheckSourceFailure(lines, "E132: Function call depth is higher than 'maxfuncdepth'", 1)
+
+  lines =<< trim END
+    vim9script
+
+    def Y0_<U>(): func(func(U): U): U
+      return (F: func(U): U) => F(Y0_<U>()(F))
+    enddef
+
+    def Foo()
+      const Partial_: func(func(any): any): any = Y0_<any>()
+      Partial_((x: any) => x)
+    enddef
+    Foo()
+  END
+  v9.CheckSourceFailure(lines, "E132: Function call depth is higher than 'maxfuncdepth'", 1)
+enddef
+
+" Test for overriding a generic object method
+def Test_generic_object_method_override()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+
+    var a = A.new()
+    var b = B.new()
+    assert_equal(10, a.Fn<number>(10))
+    assert_equal('abc', b.Fn<string>('abc'))
+    assert_equal(['a', 'b'], a.Fn<list<string>>(['a', 'b']))
+    assert_equal({a: 10, b: 20}, b.Fn<dict<number>>({a: 10, b: 20}))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for failures in overriding a generic object method
+def Test_generic_object_method_override_fails()
+  var lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+
+    class B extends A
+      def Fn(t: number): number
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1432: Overriding generic method "Fn" in class "A" with a concrete method', 13)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn(t: number): number
+        return t
+      enddef
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1433: Overriding concrete method "Fn" in class "A" with a generic method', 13)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+
+    class B extends A
+      def Fn<X, Y>(t: X): X
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1434: Mismatched number of type variables for generic method  "Fn" in class "A"', 13)
+
+  lines =<< trim END
+    vim9script
+
+    class A
+      def Fn<X, Y>(t: X): X
+        return t
+      enddef
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1434: Mismatched number of type variables for generic method  "Fn" in class "A"', 13)
+enddef
+
+" Test for overriding a generic abstract object method
+def Test_generic_abstract_object_method_override()
+  var lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def Fn<T>(t: T): T
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+
+    var b = B.new()
+    assert_equal('abc', b.Fn<string>('abc'))
+    assert_equal({a: 10, b: 20}, b.Fn<dict<number>>({a: 10, b: 20}))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for failures in overriding a generic abstract method
+def Test_generic_abstract_method_override_fails()
+  var lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def Fn<T>(t: T): T
+    endclass
+
+    class B extends A
+      def Fn(t: number): number
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1432: Overriding generic method "Fn" in class "A" with a concrete method', 11)
+
+  lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def Fn(t: number): number
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1433: Overriding concrete method "Fn" in class "A" with a generic method', 11)
+
+  lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def Fn<T>(t: T): T
+    endclass
+
+    class B extends A
+      def Fn<X, Y>(t: X): X
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1434: Mismatched number of type variables for generic method  "Fn" in class "A"', 11)
+
+  lines =<< trim END
+    vim9script
+
+    abstract class A
+      abstract def Fn<X, Y>(t: X): X
+    endclass
+
+    class B extends A
+      def Fn<T>(t: T): T
+        return t
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1434: Mismatched number of type variables for generic method  "Fn" in class "A"', 11)
+enddef
+
+" Test for using a generic method to initialize an object member variable
+def Test_generic_method_in_object_member_init_expr()
+  var lines =<< trim END
+    vim9script
+
+    def Id<T>(o: T): T
+      return o
+    enddef
+
+    class Test
+      def Id<T>(o: T): T
+        return o
+      enddef
+
+      # call the script-local "Id" function
+      const name: string = Id<string>('Test')
+
+      # call the object "Id" method
+      const hello: string = this.Id<string>('hello')
+    endclass
+
+    var t = Test.new()
+    assert_equal('Test', t.name)
+    assert_equal('hello', t.hello)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using generic type variables in the enum constructor
+def Test_generic_enum_constructor()
+  var lines =<< trim END
+    vim9script
+
+    enum CommonPair
+      HelloWorld<string, string>('hello', 'world'),
+      Booleans<bool, bool>(true, false)
+
+      const _fst: any
+      const _snd: any
+
+      def new<T, U>(fst: T, snd: U)
+        this._fst = fst
+        this._snd = snd
+      enddef
+
+      def string(): string
+        return printf("(%s, %s)", this._fst, this._snd)
+      enddef
+    endenum
+
+    assert_equal('(hello, world)', string(CommonPair.HelloWorld))
+    assert_equal('(true, false)', string(CommonPair.Booleans))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for errors in using generic type variables in the enum constructor
+def Test_generic_enum_constructor_error()
+  var lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld<string>('hello')
+      def new(a: string)
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: new', 6)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld<string>('hello')
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1560: Not a generic function: new', 4)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld('hello')
+      def new<T>(a: T)
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1559: Type arguments missing for generic function 'new'", 6)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld<string, string>('hello')
+      def new<T>(a: T)
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1556: Too many types specified for generic function 'new'", 6)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld<string>('hello')
+      def new<A, B>(a: A)
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1557: Not enough types specified for generic function 'new'", 6)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      One<string>(),
+      HelloWorld<>()
+      def new<T>()
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function '<>()'", 4)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      HelloWorld<number>()
+      def new<>()
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1555: Empty type list specified for generic function 'new'", 4)
+
+  lines =<< trim END
+    vim9script
+    enum Foo
+      One<string>(),
+      Two<A>()
+      def new<T>()
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1010: Type not recognized: A", 4)
+enddef
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index b9093a0d295b7bfcb62088cb1a66bdbaaac7d5d5..dbd6a4ef151c598de5bd8da5e2a10b3d6d950934 100644 (file)
@@ -521,12 +521,13 @@ err_ret:
  */
     static int
 parse_argument_types(
-       ufunc_T *fp,
-       garray_T *argtypes,
-       int varargs,
-       garray_T *arg_objm,
-       ocmember_T *obj_members,
-       int obj_member_count)
+    ufunc_T    *fp,
+    garray_T   *argtypes,
+    int                varargs,
+    garray_T   *arg_objm,
+    ocmember_T *obj_members,
+    int                obj_member_count,
+    cctx_T     *cctx)
 {
     int len = 0;
 
@@ -570,7 +571,7 @@ parse_argument_types(
                        }
                    }
                    else
-                       type = parse_type(&p, &fp->uf_type_list, TRUE);
+                       type = parse_type(&p, &fp->uf_type_list, fp, cctx, TRUE);
                }
                if (type == NULL)
                    return FAIL;
@@ -606,7 +607,7 @@ parse_argument_types(
            fp->uf_va_type = &t_list_any;
        else
        {
-           fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE);
+           fp->uf_va_type = parse_type(&p, &fp->uf_type_list, fp, cctx, TRUE);
            if (fp->uf_va_type != NULL && fp->uf_va_type->tt_type != VAR_LIST)
            {
                semsg(_(e_variable_arguments_type_must_be_list_str),
@@ -622,7 +623,7 @@ parse_argument_types(
 }
 
     static int
-parse_return_type(ufunc_T *fp, char_u *ret_type)
+parse_return_type(ufunc_T *fp, char_u *ret_type, cctx_T *cctx)
 {
     if (ret_type == NULL)
        fp->uf_ret_type = &t_void;
@@ -630,7 +631,7 @@ parse_return_type(ufunc_T *fp, char_u *ret_type)
     {
        char_u *p = ret_type;
 
-       fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
+       fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, fp, cctx, TRUE);
        if (fp->uf_ret_type == NULL)
        {
            fp->uf_ret_type = &t_void;
@@ -697,8 +698,7 @@ get_lambda_name(void)
     n = vim_snprintf((char *)lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
     if (n < 1)
        ret.length = 0;
-    else
-    if (n >= (int)sizeof(lambda_name))
+    else if (n >= (int)sizeof(lambda_name))
        ret.length = sizeof(lambda_name) - 1;
     else
        ret.length = (size_t)n;
@@ -1211,6 +1211,12 @@ get_function_body(
                    p = skipwhite(p + 1);
                p += eval_fname_script(p);
                vim_free(trans_function_name(&p, NULL, TRUE, 0));
+               if (vim9_function && *p == '<')
+               {
+                   // skip generic function
+                   if (skip_generic_func_type_args(&p) == FAIL)
+                       goto theend;
+               }
                if (*skipwhite(p) == '(')
                {
                    if (nesting == MAX_FUNC_NESTING - 1)
@@ -1605,14 +1611,15 @@ lambda_function_body(
     SOURCING_LNUM = sourcing_lnum_top;
 
     // parse argument types
-    if (parse_argument_types(ufunc, argtypes, varargs, NULL, NULL, 0) == FAIL)
+    if (parse_argument_types(ufunc, argtypes, varargs, NULL, NULL, 0,
+               NULL) == FAIL)
     {
        SOURCING_LNUM = lnum_save;
        goto erret;
     }
 
     // parse the return type, if any
-    if (parse_return_type(ufunc, ret_type) == FAIL)
+    if (parse_return_type(ufunc, ret_type, NULL) == FAIL)
        goto erret;
 
     pt = ALLOC_CLEAR_ONE(partial_T);
@@ -1665,7 +1672,8 @@ get_lambda_tv(
        char_u      **arg,
        typval_T    *rettv,
        int         types_optional,
-       evalarg_T   *evalarg)
+       evalarg_T   *evalarg,
+       cctx_T      *cctx)
 {
     int                evaluate = evalarg != NULL
                                      && (evalarg->eval_flags & EVAL_EVALUATE);
@@ -1849,12 +1857,13 @@ get_lambda_tv(
        if (types_optional)
        {
            if (parse_argument_types(fp, &argtypes,
-                               vim9script && varargs, NULL, NULL, 0) == FAIL)
+                               vim9script && varargs, NULL, NULL, 0,
+                               cctx) == FAIL)
                goto errret;
            if (ret_type != NULL)
            {
-               fp->uf_ret_type = parse_type(&ret_type,
-                                                     &fp->uf_type_list, TRUE);
+               fp->uf_ret_type = parse_type(&ret_type, &fp->uf_type_list,
+                                            NULL, cctx, TRUE);
                if (fp->uf_ret_type == NULL)
                    goto errret;
            }
@@ -2005,10 +2014,20 @@ deref_func_name(
                return (char_u *)"";    // just in case
            }
            s = tv->vval.v_string;
-           *lenp = (int)STRLEN(s);
+           char_u *p = generic_func_find_open_bracket(s);
+           if (p != NULL && generic_func_find_open_bracket(name) != NULL)
+           {
+               // the funcref name already has generic types but types are
+               // specified when using the funcref.
+               *lenp = 0;
+               return (char_u *)"";
+           }
+           if (p != NULL)
+               *lenp = p - s;
+           else
+               *lenp = (int)STRLEN(s);
        }
-
-       if (tv->v_type == VAR_PARTIAL)
+       else if (tv->v_type == VAR_PARTIAL)
        {
            partial_T *pt = tv->vval.v_partial;
 
@@ -2439,6 +2458,7 @@ func_requires_g_prefix(ufunc_T *ufunc)
 {
     return func_is_global(ufunc)
            && (ufunc->uf_flags & FC_LAMBDA) == 0
+           && !IS_GENERIC_FUNC(ufunc)
            && vim_strchr(ufunc->uf_name, AUTOLOAD_CHAR) == NULL
            && !SAFE_isdigit(ufunc->uf_name[0]);
 }
@@ -2710,6 +2730,9 @@ func_clear_items(ufunc_T *fp)
     VIM_CLEAR(fp->uf_va_name);
     clear_func_type_list(&fp->uf_type_list, &fp->uf_func_type);
 
+    if (IS_GENERIC_FUNC(fp))
+       generic_func_clear_items(fp);
+
     // Increment the refcount of this function to avoid it being freed
     // recursively when the partial is freed.
     fp->uf_refcount += 3;
@@ -3717,7 +3740,20 @@ func_call(
 
     if (item == NULL)
     {
-       funcexe_T funcexe;
+       funcexe_T       funcexe;
+       int             namelen = -1;
+
+       if (in_vim9script())
+       {
+           char_u *p = generic_func_find_open_bracket(name);
+
+           if (p != NULL)
+           {
+               if (skip_generic_func_type_args(&p) == FAIL)
+                   goto done;
+               namelen = p - name + 1;
+           }
+       }
 
        CLEAR_FIELD(funcexe);
        funcexe.fe_firstline = curwin->w_cursor.lnum;
@@ -3731,13 +3767,14 @@ func_call(
                ++funcexe.fe_object->obj_refcount;
        }
        funcexe.fe_selfdict = selfdict;
-       r = call_func(name, -1, rettv, argc, argv, &funcexe);
+       r = call_func(name, namelen, rettv, argc, argv, &funcexe);
     }
 
     // Free the arguments.
     while (argc > 0)
        clear_tv(&argv[--argc]);
 
+done:
     return r;
 }
 
@@ -3784,7 +3821,8 @@ call_callback(
            ++funcexe.fe_object->obj_refcount;
     }
     ++callback_depth;
-    ret = call_func(callback->cb_name, len, rettv, argcount, argvars, &funcexe);
+    ret = call_func(callback->cb_name, len, rettv, argcount, argvars,
+                                                       &funcexe);
     --callback_depth;
 
     // When a :def function was called that uses :try an error would be turned
@@ -3926,11 +3964,14 @@ call_func(
     partial_T  *partial = funcexe->fe_partial;
     type_T     check_type;
     type_T     *check_type_args[MAX_FUNC_ARGS];
+    gfargs_tab_T gfatab;
 
     // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
     // even when call_func() returns FAIL.
     rettv->v_type = VAR_UNKNOWN;
 
+    generic_func_args_table_init(&gfatab);
+
     if (partial != NULL)
        fp = partial->pt_func;
     if (fp == NULL)
@@ -3938,6 +3979,19 @@ call_func(
 
     if (fp == NULL)
     {
+       if (in_vim9script())
+       {
+           char_u *p = generic_func_find_open_bracket(funcname);
+
+           if (p != NULL)
+           {
+               len = p - funcname;
+               if (parse_generic_func_type_args(funcname, len, p, &gfatab,
+                                               funcexe->fe_cctx) == NULL)
+                   goto theend;
+           }
+       }
+
        // Make a copy of the name, if it comes from a funcref variable it
        // could be changed or deleted in the called function.
        name = len > 0 ? vim_strnsave(funcname, len) : vim_strsave(funcname);
@@ -4052,6 +4106,24 @@ call_func(
                    fp = find_func(p, is_global);
            }
 
+           if (partial == NULL && fp != NULL && IS_GENERIC_FUNC(fp))
+           {
+               // generic function call
+               fp = generic_func_get(fp, &gfatab);
+               if (fp == NULL)
+               {
+                   error = FCERR_FAILED;
+                   goto theend;
+               }
+           }
+           else if (generic_func_args_table_size(&gfatab) > 0)
+           {
+               emsg_funcname(fp != NULL ? e_not_a_generic_function_str
+                               : e_unknown_generic_function_str, rfname);
+               error = FCERR_FAILED;
+               goto theend;
+           }
+
            if (fp != NULL && (fp->uf_flags & FC_DELETED))
                error = FCERR_DELETED;
            else if (fp != NULL)
@@ -4087,6 +4159,7 @@ call_func(
                if (need_arg_check)
                    error = may_check_argument_types(funcexe, argvars, argcount,
                                       TRUE, (name != NULL) ? name : funcname);
+
                if (error == FCERR_NONE || error == FCERR_UNKNOWN)
                    error = call_user_func_check(fp, argcount, argvars, rettv,
                                                            funcexe, selfdict);
@@ -4139,6 +4212,7 @@ theend:
 
     vim_free(tofree);
     vim_free(name);
+    generic_func_args_table_clear(&gfatab);
 
     return ret;
 }
@@ -4966,7 +5040,8 @@ define_function(
        garray_T    *lines_to_free,
        int         class_flags,
        ocmember_T  *obj_members,
-       int         obj_member_count)
+       int         obj_member_count,
+       cctx_T      *cctx)
 {
     int                j;
     int                saved_did_emsg = FALSE;
@@ -4982,6 +5057,7 @@ define_function(
     garray_T   arg_objm;
     garray_T   default_args;
     garray_T   newlines;
+    gfargs_tab_T gfatab;
     int                varargs = FALSE;
     int                flags = 0;
     char_u     *ret_type = NULL;
@@ -5021,6 +5097,7 @@ define_function(
     ga_init(&argtypes);
     ga_init(&arg_objm);
     ga_init(&default_args);
+    generic_func_args_table_init(&gfatab);
 
     /*
      * Get the function name.  There are these situations:
@@ -5061,6 +5138,7 @@ define_function(
                                                                     eap->arg);
                return NULL;
            }
+
            p = eap->arg;
        }
 
@@ -5122,6 +5200,18 @@ define_function(
        goto ret_free;
     }
 
+    /*
+     * :function func<type, type..>() is a generic function
+     */
+    p = skipwhite(p);
+    if (vim9script && eap->cmdidx == CMD_def && *p == '<')
+    {
+       // generic function
+       p = parse_generic_func_type_params(name, p, &gfatab, cctx);
+       if (p == NULL)
+           goto ret_free;
+    }
+
     /*
      * ":function name(arg1, arg2)" Define function.
      */
@@ -5551,6 +5641,13 @@ define_function(
 
        fp->uf_def_status = UF_TO_BE_COMPILED;
 
+       if (generic_func_args_table_size(&gfatab) > 0)
+       {
+           // initialize generic function state
+           flags |= FC_GENERIC;
+           generic_func_init(fp, &gfatab);
+       }
+
        // error messages are for the first function line
        SOURCING_LNUM = sourcing_lnum_top;
 
@@ -5565,7 +5662,7 @@ define_function(
        is_export = FALSE;
 
        if (parse_argument_types(fp, &argtypes, varargs, &arg_objm,
-                                       obj_members, obj_member_count) == FAIL)
+                               obj_members, obj_member_count, cctx) == FAIL)
        {
            SOURCING_LNUM = lnum_save;
            free_fp = fp_allocated;
@@ -5575,7 +5672,7 @@ define_function(
        varargs = FALSE;
 
        // parse the return type, if any
-       if (parse_return_type(fp, ret_type) == FAIL)
+       if (parse_return_type(fp, ret_type, cctx) == FAIL)
        {
            SOURCING_LNUM = lnum_save;
            free_fp = fp_allocated;
@@ -5674,6 +5771,10 @@ errret_keep:
 ret_free:
     ga_clear_strings(&argtypes);
     ga_clear(&arg_objm);
+    // The generic types are still in use and should not be freed.  Instead
+    // clear the grow array.
+    ga_clear(&gfatab.gfat_param_types);
+    generic_func_args_table_clear(&gfatab);
     vim_free(fudi.fd_newkey);
     if (name != name_arg)
        vim_free(name);
@@ -5692,7 +5793,7 @@ ex_function(exarg_T *eap)
     garray_T lines_to_free;
 
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
-    (void)define_function(eap, NULL, &lines_to_free, 0, NULL, 0);
+    (void)define_function(eap, NULL, &lines_to_free, 0, NULL, 0, NULL);
     ga_clear_strings(&lines_to_free);
 }
 
@@ -5750,6 +5851,8 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
     char_u     *fname;
     ufunc_T    *ufunc;
     int                is_global = FALSE;
+    char_u     *bracket_start = NULL;
+    char_u     *generic_func_name = name;
 
     if (STRNCMP(arg, "profile", 7) == 0 && VIM_ISWHITE(arg[7]))
     {
@@ -5784,6 +5887,16 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
            vim_free(fname);
            return ufunc;
        }
+       if (fname != NULL && *arg == '<')
+       {
+           generic_func_name = name;
+           bracket_start = arg;
+           if (skip_generic_func_type_args(&arg) == FAIL)
+           {
+               vim_free(fname);
+               return NULL;
+           }
+       }
     }
     if (fname == NULL)
     {
@@ -5805,8 +5918,31 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
        if (p != NULL)
            // Try again without making it script-local.
            ufunc = find_func(p, FALSE);
+
+       if (ufunc == NULL)
+       {
+           bracket_start = generic_func_find_open_bracket(fname);
+           if (bracket_start != NULL)
+           {
+               generic_func_name = fname;
+               *bracket_start = NUL;
+               ufunc = find_func(fname, FALSE);
+               *bracket_start = '<';
+           }
+       }
     }
+
+    if (ufunc != NULL)
+    {
+       // handle generic functions
+       if (bracket_start == NULL)
+           bracket_start = generic_func_name + STRLEN(generic_func_name);
+
+       ufunc = eval_generic_func(ufunc, generic_func_name, &bracket_start);
+    }
+
     vim_free(fname);
+
     if (ufunc == NULL)
        semsg(_(e_cannot_find_function_str), name);
     return ufunc;
@@ -6044,14 +6180,18 @@ get_user_func_name(expand_T *xp, int idx)
  * Returns NULL when out of memory.
  */
     ufunc_T *
-copy_function(ufunc_T *fp)
+copy_function(ufunc_T *fp, int extra_namelen)
 {
-    ufunc_T *ufunc = alloc_ufunc(fp->uf_name, fp->uf_namelen);
+    ufunc_T *ufunc = alloc_ufunc(fp->uf_name, fp->uf_namelen + extra_namelen);
     if (ufunc == NULL)
        return NULL;
 
     // Most things can just be copied.
+    // The call to alloc_ufunc() above allocates a new uf_name_exp.  So save
+    // and restore it.
+    char_u *save_uf_name_exp = ufunc->uf_name_exp;
     *ufunc = *fp;
+    ufunc->uf_name_exp = save_uf_name_exp;
 
     ufunc->uf_def_status = UF_TO_BE_COMPILED;
     ufunc->uf_dfunc_idx = 0;
@@ -6082,6 +6222,9 @@ copy_function(ufunc_T *fp)
 
     // TODO:   partial_T       *uf_partial;
 
+    // copy generic function related state
+    copy_generic_function(fp, ufunc);
+
     if (ufunc->uf_va_name != NULL)
        ufunc->uf_va_name = vim_strsave(ufunc->uf_va_name);
 
@@ -6686,6 +6829,16 @@ ex_call(exarg_T *eap)
                                vim9script && type == NULL ? &type : NULL,
                                                     FALSE, FALSE, &found_var);
 
+    if (*arg == '<')
+    {
+       // generic function call
+       name = append_generic_func_type_args(name, STRLEN(name), &arg);
+       if (name == NULL)
+           goto end;
+       vim_free(tofree);
+       tofree = name;
+    }
+
     // Skip white space to allow ":call func ()".  Not good, but required for
     // backward compatibility.
     startarg = skipwhite(arg);
index 11357b22e0477d8acfade999dc30e92408824ae9..753be3bcc4eaecb40a9d19382ed1f3b89d7d273e 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1577,
 /**/
     1576,
 /**/
index 991ebbce1c6f8087a3125f9515517c2c6e1ff4c7..aa68a6dc8aeeaae583ede243ae624ccf8953968d 100644 (file)
@@ -101,7 +101,7 @@ parse_member(
            return FAIL;
        }
        type_arg = skipwhite(colon + 1);
-       type = parse_type(&type_arg, type_list, TRUE);
+       type = parse_type(&type_arg, type_list, NULL, NULL, TRUE);
        if (type == NULL)
            return FAIL;
        *has_type = TRUE;
@@ -358,6 +358,32 @@ validate_extends_class(
     return success;
 }
 
+/*
+ * Check a generic method is extended by a generic method and the number of
+ * type variables match.
+ */
+    static int
+validate_extends_generic_method(
+    class_T *super_cl,
+    ufunc_T *super_fp,
+    ufunc_T *cl_fp)
+{
+    char       *msg = NULL;
+
+    if (cl_fp->uf_generic_argcount == super_fp->uf_generic_argcount)
+       return TRUE;
+
+    if (super_fp->uf_generic_argcount == 0)
+       msg = e_concrete_method_str_override_with_generic_method_in_class_str;
+    else if (cl_fp->uf_generic_argcount == 0)
+       msg = e_generic_method_str_override_with_concrete_method_in_class_str;
+    else
+       msg = e_generic_method_str_type_arguments_mismatch_in_class_str;
+
+    semsg(_(msg), cl_fp->uf_name, super_cl->class_name);
+    return FALSE;
+}
+
 /*
  * Check method names in the parent class lineage to make sure the access is
  * the same for overridden methods.
@@ -397,13 +423,20 @@ validate_extends_methods(
                int     priv_method = (*qstr == '_');
                if (priv_method)
                    qstr++;
-               if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private)
+               if (STRCMP(pstr, qstr) == 0)
                {
-                   // Method access is different between the super class and
-                   // the subclass
-                   semsg(_(e_method_str_of_class_str_has_different_access),
-                           cl_fp[j]->uf_name, super->class_name);
-                   return FALSE;
+                   if (priv_method != extends_private)
+                   {
+                       // Method access is different between the super class
+                       // and the subclass
+                       semsg(_(e_method_str_of_class_str_has_different_access),
+                               cl_fp[j]->uf_name, super->class_name);
+                       return FALSE;
+                   }
+
+                   if (!validate_extends_generic_method(super,
+                                               extends_methods[i], cl_fp[j]))
+                           return FALSE;
                }
            }
        }
@@ -1436,7 +1469,8 @@ add_default_constructor(
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
 
     ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
-                           cl->class_obj_members, cl->class_obj_member_count);
+                           cl->class_obj_members, cl->class_obj_member_count,
+                           NULL);
 
     ga_clear_strings(&lines_to_free);
     vim_free(fga.ga_data);
@@ -1519,7 +1553,7 @@ add_classfuncs_objmethods(
                // methods may be overruled, then "super.Method()" is used to
                // find a method from the parent.
                ufunc_T *pf = (extends_cl->class_obj_methods)[i];
-               (*fup)[gap->ga_len + i] = copy_function(pf);
+               (*fup)[gap->ga_len + i] = copy_function(pf, 0);
 
                // If the child class overrides a function from the parent
                // the signature must be equal.
@@ -1671,7 +1705,7 @@ enum_parse_values(
        p = skipwhite(eni_name_end);
 
        char_u  *init_expr = NULL;
-       if (*p == '(')
+       if (*p == '(' || *p == '<')
        {
            if (VIM_ISWHITE(p[-1]))
            {
@@ -1684,7 +1718,7 @@ enum_parse_values(
            p = eni_name_start;
            (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
 
-           while (*expr_start && *expr_start != '(')
+           while (*expr_start && *expr_start != '(' && *expr_start != '<')
                expr_start++;
 
            if (expr_end > expr_start)
@@ -2381,8 +2415,7 @@ early_ret:
        //        enddef
        //        static def ClassFunction()
        //        enddef
-       // TODO:
-       //        def <Tval> someMethod()
+       //        def someMethod<typeA, typeB>()
        //        enddef
        else if (checkforcmd(&p, "def", 3))
        {
@@ -2434,7 +2467,8 @@ early_ret:
            else
                class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
            ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
-                       class_flags, objmembers.ga_data, objmembers.ga_len);
+                       class_flags, objmembers.ga_data, objmembers.ga_len,
+                       NULL);
            ga_clear_strings(&lines_to_free);
 
            if (uf != NULL)
@@ -2842,7 +2876,7 @@ ex_type(exarg_T *eap)
     }
 
     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
-    type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
+    type_T *type = parse_type(&arg, &si->sn_type_list, NULL, NULL, TRUE);
     if (type == NULL)
        return;
 
@@ -2958,6 +2992,7 @@ call_oc_method(
     char_u     *name_end,
     evalarg_T  *evalarg,
     char_u     **arg,
+    gfargs_tab_T       *gfatab,        // generic types
     typval_T   *rettv)
 {
     ufunc_T    *fp;
@@ -3006,6 +3041,14 @@ call_oc_method(
        return FAIL;
     }
 
+    // process generic function call
+    if (fp != NULL)
+    {
+       fp = generic_func_get(fp, gfatab);
+       if (fp == NULL)
+           return FAIL;
+    }
+
     char_u *argp = name_end;
     int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount, FALSE);
     if (ret == FAIL)
@@ -3019,6 +3062,8 @@ call_oc_method(
        funcexe.fe_object = rettv->vval.v_object;
        ++funcexe.fe_object->obj_refcount;
     }
+    if (evalarg != NULL)
+       funcexe.fe_cctx = evalarg->eval_cctx;
 
     // Clear the class or object after calling the function, in
     // case the refcount is one.
@@ -3091,6 +3136,8 @@ class_object_index(
     evalarg_T  *evalarg,
     int                verbose UNUSED) // give error messages
 {
+    int        ret = FAIL;
+
     if (VIM_ISWHITE((*arg)[1]))
     {
        semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
@@ -3124,10 +3171,28 @@ class_object_index(
        return FAIL;
     }
 
+    gfargs_tab_T    gfatab;
+
+    generic_func_args_table_init(&gfatab);
+
+    if (*name_end == '<')
+    {
+       cctx_T      *cctx = NULL;
+
+       if (evalarg != NULL)
+           cctx = evalarg->eval_cctx;
+
+       // calling a generic method
+       name_end = parse_generic_func_type_args(name, len, name + len,
+                                               &gfatab, cctx);
+       if (name_end == NULL)
+           goto done;
+    }
+
     if (*name_end == '(')
        // Invoke the class or object method
-       return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
-
+       ret = call_oc_method(cl, name, len, name_end, evalarg, arg,
+                                                       &gfatab, rettv);
     else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
     {
        // Search in the object member variable table and the class member
@@ -3136,7 +3201,8 @@ class_object_index(
        if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
        {
            *arg = name_end;
-           return OK;
+           ret = OK;
+           goto done;
        }
 
        // could be a class method or an object method
@@ -3148,22 +3214,26 @@ class_object_index(
            if (*name == '_')
            {
                semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
-               return FAIL;
+               goto done;
            }
 
            if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
                                                NULL, fp, rettv) == FAIL)
-               return FAIL;
+               goto done;
 
            *arg = name_end;
-           return OK;
+           ret = OK;
+           goto done;
        }
 
        if (did_emsg == did_emsg_save)
            member_not_found_msg(cl, rettv->v_type, name, len);
     }
 
-    return FAIL;
+done:
+    generic_func_args_table_clear(&gfatab);
+
+    return ret;
 }
 
 /*
@@ -3200,6 +3270,13 @@ find_class_func(char_u **arg)
 
     fp = method_lookup(cl, tv.v_type, fname, len, NULL);
 
+    if (fp != NULL)
+    {
+       fp = eval_generic_func(fp, fname, &fname_end);
+       if (fp != NULL)
+           *arg = fname_end;
+    }
+
 fail_after_eval:
     clear_tv(&tv);
     return fp;
index d4ffb9331307dbf275d10e854c14ab9db4590806..4486213adf40035421fbb4c4d84769cc85f8aa07 100644 (file)
@@ -292,7 +292,7 @@ compile_lock_unlock(
 #ifdef LOG_LOCKVAR
            ch_log(NULL, "LKVAR:    ... INS_LOCKUNLOCK %s", name);
 #endif
-           if (compile_load(&name, name + len, cctx, FALSE, FALSE) == FAIL)
+           if (compile_load(&name, len, name + len, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
            isn = ISN_LOCKUNLOCK;
        }
@@ -1130,7 +1130,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
                    goto failed;
                }
                p = skipwhite(p + 1);
-               lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
+               lhs_type = parse_type(&p, cctx->ctx_type_list, cctx->ctx_ufunc,
+                                                               cctx, TRUE);
                if (lhs_type == NULL)
                    goto failed;
            }
index a7c412e51d1f52c8e32f0e2e02b38d8632c88f8d..8b7064bd7b326b5818a8b6dd733568371dc179cb 100644 (file)
@@ -1032,6 +1032,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
     compiletype_T   compile_type;
     int                funcref_isn_idx = -1;
     lvar_T     *lvar = NULL;
+    char_u     *bracket_start = NULL;
 
     if (eap->forceit)
     {
@@ -1047,6 +1048,14 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
            ++name_end;
        set_nextcmd(eap, name_end);
     }
+
+    if (*name_end == '<')
+    {
+       bracket_start = name_end;
+       if (skip_generic_func_type_args(&name_end) == FAIL)
+           return NULL;
+    }
+
     if (name_end == name_start || *skipwhite(name_end) != '(')
     {
        if (!ends_excmd2(name_start, name_end))
@@ -1065,6 +1074,11 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
        return eap->nextcmd == NULL ? (char_u *)"" : eap->nextcmd;
     }
 
+    if (bracket_start != NULL)
+       // generic function.  The function name ends before the list of types
+       // (opening angle bracket).
+       name_end = bracket_start;
+
     // Only g:Func() can use a namespace.
     if (name_start[1] == ':' && !is_global)
     {
@@ -1104,7 +1118,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
     int save_KeyTyped = KeyTyped;
     KeyTyped = FALSE;
 
-    ufunc = define_function(eap, lambda_name.string, lines_to_free, 0, NULL, 0);
+    ufunc = define_function(eap, lambda_name.string, lines_to_free, 0, NULL, 0,
+                                                                       cctx);
 
     KeyTyped = save_KeyTyped;
 
@@ -2105,7 +2120,7 @@ compile_lhs_set_type(cctx_T *cctx, lhs_T *lhs, char_u *var_end, int is_decl)
        }
 
        p = skipwhite(var_end + 1);
-       lhs->lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
+       lhs->lhs_type = parse_type(&p, cctx->ctx_type_list, cctx->ctx_ufunc, cctx, TRUE);
        if (lhs->lhs_type == NULL)
            return FAIL;
 
index 55f9d4327520e4daca68059e045d40c2c7bc7851..9d5709e2574c47e4e20496e141c9b317ee261f30 100644 (file)
@@ -1522,6 +1522,14 @@ call_by_name(
        return call_bfunc(func_idx, argcount, ectx);
     }
 
+    char_u     cc = NUL;
+    char_u     *start_bracket = generic_func_find_open_bracket(name);
+    if (start_bracket != NULL)
+    {
+       cc = *start_bracket;
+       *start_bracket = NUL;
+    }
+
     ufunc = find_func(name, FALSE);
 
     if (ufunc == NULL)
@@ -1533,11 +1541,36 @@ call_by_name(
            ufunc = find_func(name, FALSE);
 
        if (vim9_aborting(prev_uncaught_emsg))
+       {
+           if (start_bracket != NULL)
+               *start_bracket = cc;
            return FAIL;  // bail out if loading the script caused an error
+       }
     }
 
+    if (start_bracket != NULL)
+       *start_bracket = cc;
+
     if (ufunc != NULL)
     {
+       if (IS_GENERIC_FUNC(ufunc))
+       {
+           if (start_bracket != NULL)
+               ufunc = find_generic_func(ufunc, name, &start_bracket);
+           else
+           {
+               emsg_funcname(e_generic_func_missing_type_args_str, name);
+               ufunc = NULL;
+           }
+           if (ufunc == NULL)
+               return FAIL;
+       }
+       else if (start_bracket != NULL)
+       {
+           emsg_funcname(e_not_a_generic_function_str, name);
+           return FAIL;
+       }
+
        if (check_ufunc_arg_types(ufunc, argcount, 0, ectx) == FAIL)
            return FAIL;
 
@@ -5068,7 +5101,7 @@ exec_instructions(ectx_T *ectx)
                    ea.cmd = ea.arg = iptr->isn_arg.string;
                    ga_init2(&lines_to_free, sizeof(char_u *), 50);
                    SOURCING_LNUM = iptr->isn_lnum;
-                   define_function(&ea, NULL, &lines_to_free, 0, NULL, 0);
+                   define_function(&ea, NULL, &lines_to_free, 0, NULL, 0, NULL);
                    ga_clear_strings(&lines_to_free);
                }
                break;
index 715771c7dc02ab8f4d49e1fdf8893d7da8f89376..f3aa4b1f5cef4e4bee2098e65111b7bfa28401df 100644 (file)
@@ -355,7 +355,8 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
     static int
 compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
 {
-    int m_idx;
+    int                m_idx;
+    int                ret = FAIL;
 
     if (VIM_ISWHITE((*arg)[1]))
     {
@@ -410,6 +411,19 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
     }
     size_t len = name_end - name;
 
+    gfargs_tab_T    gfatab;
+
+    generic_func_args_table_init(&gfatab);
+
+    if (*name_end == '<')
+    {
+       // generic method call
+       name_end = parse_generic_func_type_args(name, len, name_end,
+                                                       &gfatab, cctx);
+       if (name_end == NULL)
+           return FAIL;
+    }
+
     if (*name_end == '(')
     {
        int     function_count;
@@ -458,14 +472,14 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
            {
                method_not_found_msg(cl, type->tt_type, name, len);
-               return FAIL;
+               goto done;
            }
            if (type->tt_type == VAR_CLASS)
            {
                // Remove the class type from the stack
                --cctx->ctx_type_stack.ga_len;
                if (generate_CLASSMEMBER(cctx, TRUE, cl, m_idx) == FAIL)
-                   return FAIL;
+                   goto done;
            }
            else
            {
@@ -478,7 +492,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
                    status = generate_GET_OBJ_MEMBER(cctx, m_idx,
                                                        ocm->ocm_type);
                if (status == FAIL)
-                   return FAIL;
+                   goto done;
            }
        }
 
@@ -488,7 +502,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            // allowed.
            semsg(_(e_abstract_method_str_direct), ufunc->uf_name,
                    ufunc->uf_defclass->class_name);
-           return FAIL;
+           goto done;
        }
 
        // A private object method can be used only inside the class where it
@@ -502,7 +516,15 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
                     && cctx->ctx_ufunc->uf_class != cl)))
        {
            semsg(_(e_cannot_access_protected_method_str), name);
-           return FAIL;
+           goto done;
+       }
+
+       // process generic function call
+       if (ufunc != NULL)
+       {
+           ufunc = generic_func_get(ufunc, &gfatab);
+           if (ufunc == NULL)
+               goto done;
        }
 
        // Compile the arguments and call the class function or object method.
@@ -511,14 +533,19 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
        *arg = skipwhite(name_end + 1);
        int argcount = 0;
        if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
-           return FAIL;
+           goto done;
 
        if (ocm != NULL)
-           return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
+       {
+           ret = generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
+           goto done;
+       }
        if (type->tt_type == VAR_OBJECT
                     && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
-           return generate_CALL(cctx, ufunc, cl, fi, argcount, is_super);
-       return generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
+           ret = generate_CALL(cctx, ufunc, cl, fi, argcount, is_super);
+       else
+           ret = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
+       goto done;
     }
 
     if (type->tt_type == VAR_OBJECT)
@@ -530,13 +557,15 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            {
                emsg_var_cl_define(e_cannot_access_protected_variable_str,
                                                        m->ocm_name, 0, cl);
-               return FAIL;
+               goto done;
            }
 
            *arg = name_end;
            if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
-               return generate_GET_ITF_MEMBER(cctx, cl, m_idx, m->ocm_type);
-           return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type);
+               ret = generate_GET_ITF_MEMBER(cctx, cl, m_idx, m->ocm_type);
+           else
+               ret = generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type);
+           goto done;
        }
 
        // Could be an object method reference: "obj.Func".
@@ -548,12 +577,13 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            if (*name == '_' && !inside_class(cctx, cl))
            {
                semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
-               return FAIL;
+               goto done;
            }
            *arg = name_end;
            // Remove the object type from the stack
            --cctx->ctx_type_stack.ga_len;
-           return generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL);
+           ret = generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL);
+           goto done;
        }
 
        member_not_found_msg(cl, VAR_OBJECT, name, len);
@@ -572,13 +602,14 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            {
                emsg_var_cl_define(e_cannot_access_protected_variable_str,
                                                        m->ocm_name, 0, cl);
-               return FAIL;
+               goto done;
            }
 
            *arg = name_end;
            // Remove the class type from the stack
            --cctx->ctx_type_stack.ga_len;
-           return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+           ret = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+           goto done;
        }
 
        // Could be a class method reference: "class.Func".
@@ -590,18 +621,21 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
            if (*name == '_' && !inside_class(cctx, cl))
            {
                semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
-               return FAIL;
+               goto done;
            }
            *arg = name_end;
            // Remove the class type from the stack
            --cctx->ctx_type_stack.ga_len;
-           return generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL);
+           ret = generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL);
+           goto done;
        }
 
        member_not_found_msg(cl, VAR_CLASS, name, len);
     }
 
-    return FAIL;
+done:
+    generic_func_args_table_clear(&gfatab);
+    return ret;
 }
 
 /*
@@ -731,6 +765,43 @@ compile_load_scriptvar(
        {
            if (ufunc != NULL)
            {
+               gfargs_tab_T    gfatab;
+
+               generic_func_args_table_init(&gfatab);
+
+               if (IS_GENERIC_FUNC(ufunc))
+               {
+                   if (*p == '<')
+                   {
+                       // generic function call
+                       p = parse_generic_func_type_args(name, STRLEN(name), p,
+                                                               &gfatab, cctx);
+                       if (p != NULL)
+                       {
+                           *end = p;
+
+                           // generic function call
+                           ufunc = generic_func_get(ufunc, &gfatab);
+                       }
+
+                       generic_func_args_table_clear(&gfatab);
+
+                       if (p == NULL || ufunc == NULL)
+                           return FAIL;
+                   }
+                   else
+                   {
+                       emsg_funcname(e_generic_func_missing_type_args_str,
+                                                                       name);
+                       return FAIL;
+                   }
+               }
+               else if (*p == '<')
+               {
+                   emsg_funcname(e_not_a_generic_function_str, name);
+                   return FAIL;
+               }
+
                // function call or function reference
                generate_PUSHFUNC(cctx, ufunc->uf_name, NULL, TRUE);
                return OK;
@@ -752,7 +823,11 @@ compile_load_scriptvar(
 }
 
     static int
-generate_funcref(cctx_T *cctx, char_u *name, int has_g_prefix)
+generate_funcref(
+    cctx_T             *cctx,
+    char_u             *name,
+    gfargs_tab_T       *gfatab,
+    int                        has_g_prefix)
 {
     ufunc_T *ufunc = find_func(name, FALSE);
     compiletype_T compile_type;
@@ -761,6 +836,11 @@ generate_funcref(cctx_T *cctx, char_u *name, int has_g_prefix)
     if (ufunc == NULL || (!has_g_prefix && func_requires_g_prefix(ufunc)))
        return FAIL;
 
+    // process generic function call
+    ufunc = generic_func_get(ufunc, gfatab);
+    if (ufunc == NULL)
+       return FAIL;
+
     // Need to compile any default values to get the argument types.
     compile_type = get_compile_type(ufunc);
     if (func_needs_compiling(ufunc, compile_type)
@@ -798,6 +878,7 @@ compiling_a_class_method(cctx_T *cctx)
     int
 compile_load(
        char_u **arg,
+       size_t  namelen,
        char_u *end_arg,
        cctx_T  *cctx,
        int     is_expr,
@@ -808,6 +889,18 @@ compile_load(
     char_u     *end = end_arg;
     int                res = FAIL;
     int                prev_called_emsg = called_emsg;
+    gfargs_tab_T gfatab;
+
+    generic_func_args_table_init(&gfatab);
+
+    if (*(*arg + namelen) == '<')
+    {
+       // generic function call
+       if (parse_generic_func_type_args(*arg, namelen,
+                       *arg + namelen, &gfatab, cctx) == NULL)
+           return FAIL;
+       *(*arg + namelen) = NUL;
+    }
 
     if (*(*arg + 1) == ':')
     {
@@ -837,7 +930,7 @@ compile_load(
            // load namespaced variable
            name = vim_strnsave(*arg + 2, end - (*arg + 2));
            if (name == NULL)
-               return FAIL;
+               goto theend;
 
            switch (**arg)
            {
@@ -847,11 +940,10 @@ compile_load(
                          {
                              semsg(_(e_cannot_use_s_colon_in_vim9_script_str),
                                                                         *arg);
-                             vim_free(name);
-                             return FAIL;
+                             goto theend;
                          }
                          if (is_expr && find_func(name, FALSE) != NULL)
-                             res = generate_funcref(cctx, name, FALSE);
+                             res = generate_funcref(cctx, name, &gfatab, FALSE);
                          else
                              res = compile_load_scriptvar(cctx, name,
                                                            NULL, &end, NULL);
@@ -859,8 +951,10 @@ compile_load(
                case 'g': if (vim_strchr(name, AUTOLOAD_CHAR) == NULL)
                          {
                              if (is_expr && ASCII_ISUPPER(*name)
-                                      && find_func(name, FALSE) != NULL)
-                                 res = generate_funcref(cctx, name, TRUE);
+                                      && (find_func(name, FALSE) != NULL
+                                          || gfatab.gfat_args.ga_len > 0))
+                                 res = generate_funcref(cctx, name, &gfatab,
+                                                               TRUE);
                              else
                                  isn_type = ISN_LOADG;
                          }
@@ -870,7 +964,7 @@ compile_load(
                              vim_free(name);
                              name = vim_strnsave(*arg, end - *arg);
                              if (name == NULL)
-                                 return FAIL;
+                                 goto theend;
                          }
                          break;
                case 'w': isn_type = ISN_LOADW; break;
@@ -891,7 +985,6 @@ compile_load(
     }
     else
     {
-       size_t      len = end - *arg;
        int         idx;
        int         method_idx;
        int         gen_load = FALSE;
@@ -899,9 +992,9 @@ compile_load(
        int         outer_loop_depth = -1;
        int         outer_loop_idx = -1;
 
-       name = vim_strnsave(*arg, len);
+       name = vim_strnsave(*arg, namelen);
        if (name == NULL)
-           return FAIL;
+           goto theend;
 
        if (STRCMP(name, "super") == 0 && compiling_a_class_method(cctx))
        {
@@ -916,7 +1009,7 @@ compile_load(
            script_autoload(name, FALSE);
            res = generate_LOAD(cctx, ISN_LOADAUTO, 0, name, &t_any);
        }
-       else if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx)
+       else if (arg_exists(*arg, namelen, &idx, &type, &gen_load_outer, cctx)
                                                                         == OK)
        {
            if (gen_load_outer == 0)
@@ -927,7 +1020,7 @@ compile_load(
            lvar_T  lvar;
            class_T *cl = NULL;
 
-           if (lookup_local(*arg, len, &lvar, cctx) == OK)
+           if (lookup_local(*arg, namelen, &lvar, cctx) == OK)
            {
                type = lvar.lv_type;
                idx = lvar.lv_idx;
@@ -942,9 +1035,9 @@ compile_load(
            }
            else if (cctx->ctx_ufunc->uf_defclass != NULL &&
                    (((idx =
-                      cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
+                      cctx_class_member_idx(cctx, *arg, namelen, &cl)) >= 0)
                     || ((method_idx =
-                            cctx_class_method_idx(cctx, *arg, len, &cl)) >= 0)))
+                            cctx_class_method_idx(cctx, *arg, namelen, &cl)) >= 0)))
            {
                // Referencing a class variable or method without the class
                // name.  A class variable or method can be referenced without
@@ -974,7 +1067,7 @@ compile_load(
 
                // "var" can be script-local even without using "s:" if it
                // already exists in a Vim9 script or when it's imported.
-               if (script_var_exists(*arg, len, cctx, NULL) == OK
+               if (script_var_exists(*arg, namelen, cctx, NULL) == OK
                            || (imp = find_imported(name, 0, FALSE)) != NULL)
                    res = compile_load_scriptvar(cctx, name, *arg, &end, imp);
 
@@ -982,7 +1075,7 @@ compile_load(
                // uppercase letter it can be a user defined function.
                // generate_funcref() will fail if the function can't be found.
                if (res == FAIL && is_expr && ASCII_ISUPPER(*name))
-                   res = generate_funcref(cctx, name, FALSE);
+                   res = generate_funcref(cctx, name, &gfatab, FALSE);
            }
        }
        if (gen_load)
@@ -1001,6 +1094,7 @@ theend:
     if (res == FAIL && error && called_emsg == prev_called_emsg)
        semsg(_(e_variable_not_found_str), name);
     vim_free(name);
+    generic_func_args_table_clear(&gfatab);
     return res;
 }
 
@@ -1123,13 +1217,16 @@ compile_arguments(
        if (*p != ',' && *skipwhite(p) == ',')
        {
            semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
-           p = skipwhite(p);
+           return FAIL;
        }
        if (*p == ',')
        {
            ++p;
            if (*p != NUL && !VIM_ISWHITE(*p))
+           {
                semsg(_(e_white_space_required_after_str_str), ",", p - 1);
+               return FAIL;
+           }
        }
        else
            must_end = TRUE;
@@ -1280,7 +1377,23 @@ compile_call(
     else
        special_fn = CA_NOT_SPECIAL;
 
-    *arg = skipwhite(*arg + varlen + 1);
+    gfargs_tab_T    gfatab;
+
+    generic_func_args_table_init(&gfatab);
+
+    if (*(*arg + varlen) == '<')
+    {
+       // generic function
+       *arg = parse_generic_func_type_args(*arg, varlen, *arg + varlen,
+                                                               &gfatab, cctx);
+       if (*arg == NULL)
+           goto theend;
+       ++*arg;                 // skip '('
+    }
+    else
+       *arg += varlen + 1;
+
+    *arg = skipwhite(*arg);
     if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL)
        goto theend;
 
@@ -1366,15 +1479,22 @@ compile_call(
        ufunc = find_func(name, FALSE);
        if (ufunc != NULL)
        {
+           // process generic function call
+           ufunc = generic_func_get(ufunc, &gfatab);
+           if (ufunc == NULL)
+               goto theend;
+
            if (!func_is_global(ufunc))
            {
                res = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
                goto theend;
            }
            if (!has_g_namespace
-                         && vim_strchr(ufunc->uf_name, AUTOLOAD_CHAR) == NULL)
+                         && vim_strchr(ufunc->uf_name, AUTOLOAD_CHAR) == NULL
+                         && !IS_GENERIC_FUNC(ufunc))
            {
-               // A function name without g: prefix must be found locally.
+               // A function name without g: prefix must be found locally
+               // A generic function has the name emptied out.
                emsg_funcname(e_unknown_function_str, namebuf);
                goto theend;
            }
@@ -1404,7 +1524,7 @@ compile_call(
     // Not for some#Func(), it will be loaded later.
     p = namebuf;
     if (!has_g_namespace && !is_autoload
-           && compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK)
+           && compile_load(&p, varlen, namebuf + varlen, cctx, FALSE, FALSE) == OK)
     {
        type_T      *s_type = get_type_on_stack(cctx, 0);
 
@@ -1427,6 +1547,7 @@ compile_call(
        emsg_funcname(e_unknown_function_str, namebuf);
 
 theend:
+    generic_func_args_table_clear(&gfatab);
     vim_free(tofree);
     return res;
 }
@@ -1640,7 +1761,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
     evalarg.eval_cctx = cctx;
 
     // Get the funcref in "rettv".
-    r = get_lambda_tv(arg, &rettv, TRUE, &evalarg);
+    r = get_lambda_tv(arg, &rettv, TRUE, &evalarg, cctx);
     if (r != OK)
     {
        clear_evalarg(&evalarg, NULL);
@@ -1730,7 +1851,7 @@ get_lambda_tv_and_compile(
 
     // Get the funcref in "rettv".
     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
-    r = get_lambda_tv(arg, rettv, types_optional, evalarg);
+    r = get_lambda_tv(arg, rettv, types_optional, evalarg, NULL);
     current_sctx.sc_version = save_sc_version;
     if (r != OK)
        return r;  // currently unreachable
@@ -2994,16 +3115,23 @@ compile_expr9(
            return FAIL;
        }
 
+       size_t namelen = p - *arg;
+       if (*p == '<')
+       {
+           if (skip_generic_func_type_args(&p) == FAIL)
+               return FAIL;
+       }
+
        if (*p == '(')
        {
-           r = compile_call(arg, p - *arg, cctx, ppconst, 0);
+           r = compile_call(arg, namelen, cctx, ppconst, 0);
        }
        else
        {
            if (cctx->ctx_skip != SKIP_YES
                                    && generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
-           r = compile_load(arg, p, cctx, TRUE, TRUE);
+           r = compile_load(arg, namelen, p, cctx, TRUE, TRUE);
        }
        if (r == FAIL)
            return FAIL;
@@ -3039,7 +3167,7 @@ compile_expr8(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
     if (**arg == '<' && eval_isnamec1((*arg)[1]))
     {
        ++*arg;
-       want_type = parse_type(arg, cctx->ctx_type_list, TRUE);
+       want_type = parse_type(arg, cctx->ctx_type_list, cctx->ctx_ufunc, cctx, TRUE);
        if (want_type == NULL)
            return FAIL;
 
diff --git a/src/vim9generics.c b/src/vim9generics.c
new file mode 100644 (file)
index 0000000..1452610
--- /dev/null
@@ -0,0 +1,1285 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * vim9generics.c: Vim9 script generics support
+ */
+
+#include "vim.h"
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+
+
+/*
+ * A hash table is used to lookup a generic function with specific types.
+ * The specific type names are used as the key.
+ */
+typedef struct gfitem_S gfitem_T;
+struct gfitem_S
+{
+    ufunc_T    *gfi_ufunc;
+    char_u     gfi_name[1];    // actually longer
+};
+#define GFITEM_KEY_OFF offsetof(gfitem_T, gfi_name)
+#define HI2GFITEM(hi)  ((gfitem_T *)((hi)->hi_key - GFITEM_KEY_OFF))
+
+static type_T *find_generic_type_in_cctx(char_u *gt_name, size_t len, cctx_T *cctx);
+
+/*
+ * Returns a pointer to the first '<' character in "name" that starts the
+ * generic type argument list, skipping an initial <SNR> or <lambda> prefix if
+ * present.  The prefix is only skipped if "name" starts with '<'.
+ *
+ * Returns NULL if no '<' is found before a '(' or the end of the string.
+ * The returned pointer refers to the original string.
+ *
+ * Examples:
+ *   "<SNR>123_Fn<number>"    -> returns pointer to '<'
+ *   "<lambda>123_Fn<number>" -> returns pointer to '<'
+ *   "Func<number>"           -> returns pointer to '<'
+ *   "Func()"                 -> returns NULL
+ */
+    char_u *
+generic_func_find_open_bracket(char_u *name)
+{
+    char_u     *p = name;
+
+    if (name[0] == '<')
+    {
+       // Skip the <SNR> or <lambda> at the start of the name
+       if (STRNCMP(name + 1, "SNR>", 4) == 0)
+           p += 5;
+       else if (STRNCMP(name + 1, "lambda>", 7) == 0)
+           p += 8;
+    }
+
+    while (*p && *p != '(' && *p != '<')
+       p++;
+
+    if (*p == '<')
+       return p;
+
+    return NULL;
+}
+
+/*
+ * Finds the matching '>' character for a generic function type parameter or
+ * argument list, starting from the opening '<'.
+ *
+ * Enforces correct syntax for a flat, comma-separated list of types:
+ * - No whitespace before or after type names or commas
+ * - Each type must be non-empty and separated by a comma and whitespace
+ * - At least one type must be present
+ *
+ * Arguments:
+ *   start - pointer to the opening '<'
+ *
+ * Returns:
+ *   Pointer to the matching '>' character if found and syntax is valid,
+ *   or NULL if not found, invalid syntax, or on error.
+ */
+    static char_u *
+generic_func_find_close_bracket(char_u *start)
+{
+    char_u     *p = start + 1;
+    int                type_count = 0;
+
+    while (*p && *p != '>')
+    {
+       char_u  *typename = p;
+
+       if (VIM_ISWHITE(*p))
+       {
+           char tmpstr[2];
+           tmpstr[0] = *(p - 1); tmpstr[1] = NUL;
+           semsg(_(e_no_white_space_allowed_after_str_str), tmpstr, start);
+           return NULL;
+       }
+
+       p = skip_type(p, FALSE);
+       if (p == typename)
+       {
+           char_u cc = *p;
+           *p = NUL;
+           semsg(_(e_missing_type_after_str), start);
+           *p = cc;
+           return NULL;
+       }
+       type_count++;
+
+       if (*p == '>' || *p == NUL)
+           break;
+
+       if (VIM_ISWHITE(*p))
+       {
+           char_u cc = *p;
+           *p = NUL;
+           semsg(_(e_no_white_space_allowed_after_str_str), typename, start);
+           *p = cc;
+           return NULL;
+       }
+
+       if (*p != ',')
+       {
+           semsg(_(e_missing_comma_in_generic_function_str), start);
+           return NULL;
+       }
+       p++;
+
+       if (*p == NUL)
+           break;
+
+       if (!VIM_ISWHITE(*p))
+       {
+           semsg(_(e_white_space_required_after_str_str), ",", start);
+           return NULL;
+       }
+       p = skipwhite(p);
+    }
+
+    if (*p != '>')
+    {
+       semsg(_(e_missing_closing_angle_bracket_in_generic_function_str), start);
+       return NULL;
+    }
+
+    if (type_count == 0)
+    {
+       semsg(_(e_empty_type_list_for_generic_function_str), start);
+       return NULL;
+    }
+
+    return p;
+}
+
+/*
+ * Advances the argument pointer past a generic function's type argument list.
+ *
+ * On entry, "*argp" must point to the opening '<' of a generic type argument
+ * list.  This function finds the matching closing '>' (validating the syntax
+ * via generic_func_find_close_bracket), and if successful, advances "*argp" to
+ * the character immediately after the closing '>'.
+ *
+ * Returns OK on success, or FAIL if the type argument list is invalid or no
+ * matching '>' is found. On failure, "*argp" is not modified.
+ */
+    int
+skip_generic_func_type_args(char_u **argp)
+{
+    char_u *p = generic_func_find_close_bracket(*argp);
+    if (p == NULL)
+       return FAIL;
+
+    *argp = p + 1;     // skip '>'
+
+    return OK;
+}
+
+/*
+ * Appends the generic function type arguments, starting at "*argp", to the
+ * function name "funcname" (of length "namelen") and returns a newly allocated
+ * string containing the result.
+ *
+ * On entry, "*argp" must point to the opening '<' of the generic type argument
+ * list.  If the type argument list is valid, the substring from "*argp" up to
+ * and including the matching '>' is appended to "funcname". On success,
+ * "*argp" is updated to point to the character after the closing '>'.
+ *
+ * Returns:
+ *   A newly allocated string with the combined function name and type
+ *   arguments, or NULL if there is a syntax error in the generic type
+ *   arguments.
+ *
+ * The caller is responsible for freeing the returned string.
+ */
+    char_u *
+append_generic_func_type_args(
+    char_u     *funcname,
+    size_t     namelen,
+    char_u     **argp)
+{
+    char_u *p = generic_func_find_close_bracket(*argp);
+
+    if (p == NULL)
+       return NULL;
+
+    vim_strncpy(IObuff, funcname, namelen);
+    STRNCAT(IObuff, *argp, p - *argp + 1);
+
+    *argp = p + 1;
+
+    return vim_strsave(IObuff);
+}
+
+/*
+ * Returns a newly allocated string containing the function name from "fp" with
+ * the generic type arguments from "*argp" appended.
+ *
+ * On entry, "*argp" must point to the opening '<' of the generic type argument
+ * list.  On success, "*argp" is advanced to the character after the closing
+ * '>'.
+ *
+ * Returns:
+ *   A newly allocated string with the combined function name and type
+ *   arguments, or NULL if "fp" is not a generic function, if there is a
+ *   parsing error, or on memory allocation failure.
+ *
+ * The caller is responsible for freeing the returned string.
+ */
+    char_u *
+get_generic_func_name(ufunc_T *fp, char_u **argp)
+{
+    if (!IS_GENERIC_FUNC(fp))
+    {
+       emsg_funcname(e_not_a_generic_function_str, fp->uf_name);
+       return NULL;
+    }
+
+    return append_generic_func_type_args(fp->uf_name, fp->uf_namelen, argp);
+}
+
+/*
+ * Parses the concrete type arguments provided in a generic function call,
+ * starting at the opening '<' character and ending at the matching '>'.
+ *
+ * On entry, "start" must point to the opening '<' character.
+ * On success, returns a pointer to the character after the closing '>'.
+ * On failure, returns NULL and reports an error message.
+ *
+ * Arguments:
+ *   func_name - the name of the function being called (used for error
+ *               messages)
+ *   namelen   - length of the function name
+ *   start     - pointer to the opening '<' character in the call
+ *   gfatab    - args table to allocate new type objects and to store parsed
+ *               type argument names and their types.
+ *   cctx      - compile context for type resolution (may be NULL)
+ *
+ * This function enforces correct syntax for generic type argument lists,
+ * including whitespace rules, comma separation, and non-empty argument lists.
+ */
+    char_u *
+parse_generic_func_type_args(
+    char_u             *func_name,
+    size_t             namelen,
+    char_u             *start,
+    gfargs_tab_T       *gfatab,
+    cctx_T             *cctx)
+{
+    generic_T  *generic_arg;
+    type_T     *type_arg;
+    char_u     *p = start;
+
+    // White spaces not allowed after '<'
+    if (VIM_ISWHITE(*(p + 1)))
+    {
+       semsg(_(e_no_white_space_allowed_after_str_str), "<", p);
+       return NULL;
+    }
+
+    ++p;       // skip the '<'
+
+    // parse each type argument until '>' or end of string
+    while (*p && *p != '>')
+    {
+       p = skipwhite(p);
+
+       if (!ASCII_ISALNUM(*p))
+       {
+           semsg(_(e_missing_type_after_str), start);
+           return NULL;
+       }
+
+       // parse the type
+       type_arg = parse_type(&p, &gfatab->gfat_arg_types, NULL, cctx, TRUE);
+       if (type_arg == NULL)
+           return NULL;
+
+       char    *ret_free = NULL;
+       char    *ret_name = type_name(type_arg, &ret_free);
+
+       // create space for the name and the new type
+       if (ga_grow(&gfatab->gfat_args, 1) == FAIL)
+           return NULL;
+       generic_arg = (generic_T *)gfatab->gfat_args.ga_data +
+                                               gfatab->gfat_args.ga_len;
+       gfatab->gfat_args.ga_len++;
+
+       // copy the type name
+       generic_arg->gt_name = alloc(STRLEN(ret_name) + 1);
+       if (generic_arg->gt_name == NULL)
+           return NULL;
+       STRCPY(generic_arg->gt_name, ret_name);
+       vim_free(ret_free);
+
+       // add the new type
+       generic_arg->gt_type = type_arg;
+
+       p = skipwhite(p);
+
+       // after a type, expect ',' or '>'
+       if (*p != ',' && *p != '>')
+       {
+           semsg(_(e_missing_comma_in_generic_function_str), start);
+           return NULL;
+       }
+
+       // if there's a comma, require whitespace after it and skip it
+       if (*p == ',')
+       {
+           if (!VIM_ISWHITE(*(p + 1)))
+           {
+               semsg(_(e_white_space_required_after_str_str), ",", p);
+               return NULL;
+           }
+           p++;
+       }
+    }
+
+    // ensure the list of types ends in a closing '>'
+    if (*p != '>')
+       return NULL;
+
+    // no whitespace allowed before '>'
+    if (VIM_ISWHITE(*(p - 1)))
+    {
+       semsg(_(e_no_white_space_allowed_before_str_str), ">", p);
+       return NULL;
+    }
+
+    // at least one type argument is required
+    if (generic_func_args_table_size(gfatab) == 0)
+    {
+       char_u  cc = func_name[namelen];
+       func_name[namelen] = NUL;
+       semsg(_(e_empty_type_list_for_generic_function_str), func_name);
+       func_name[namelen] = cc;
+       return NULL;
+    }
+    ++p;       // skip the '>'
+
+    return p;
+}
+
+/*
+ * Checks if a generic type name already exists in the current context.
+ *
+ * This function verifies that the given generic type name "name" does not
+ * conflict with an imported variable, an existing generic type in the provided
+ * growarray "gt_gap", or a generic type in the current or outer compile
+ * context "cctx". If a conflict is found, an appropriate error message is
+ * reported.
+ *
+ * Arguments:
+ *   name    - the generic type name to check
+ *   gfatab  - args table to allocate new type objects and to store parsed
+ *             type argument names and their types.
+ *   cctx    - current compile context, used to check for outer generic types
+ *   (may be NULL)
+ *
+ * Returns:
+ *   TRUE if the name already exists or conflicts, FALSE otherwise.
+ */
+    static int
+generic_name_exists(
+    char_u             *gt_name,
+    size_t             name_len,
+    gfargs_tab_T       *gfatab,
+    cctx_T             *cctx)
+{
+    typval_T   tv;
+
+    tv.v_type = VAR_UNKNOWN;
+
+    if (eval_variable_import(gt_name, &tv) == OK)
+    {
+       semsg(_(e_redefining_script_item_str), gt_name);
+       clear_tv(&tv);
+       return TRUE;
+    }
+
+    for (int i = 0; i < gfatab->gfat_args.ga_len; i++)
+    {
+       generic_T *generic = &((generic_T *)gfatab->gfat_args.ga_data)[i];
+
+       if (STRNCMP(gt_name, generic->gt_name, name_len) == 0)
+       {
+           semsg(_(e_duplicate_type_var_name_str), gt_name);
+           return TRUE;
+       }
+    }
+
+    if (cctx != NULL &&
+           find_generic_type_in_cctx(gt_name, name_len, cctx) != NULL)
+    {
+       semsg(_(e_duplicate_type_var_name_str), gt_name);
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * Parses the type parameters specified when defining a new generic function,
+ * starting at the opening '<' character and ending at the matching '>'.
+ *
+ * On entry, "p" must point to the opening '<' character.
+ * On success, returns a pointer to the character after the closing '>'.
+ * On failure, returns NULL and reports an error message.
+ *
+ * Arguments:
+ *   func_name - the name of the function being defined (for error messages)
+ *   p         - pointer to the opening '<' character in the definition
+ *   gfatab    - args table to allocate new type objects and to store parsed
+ *               type argument names and their types.
+ *   cctx      - current compile context, used to check for duplicate names in
+ *              outer scopes (may be NULL)
+ *
+ * This function enforces correct syntax for generic type parameter lists:
+ * - No whitespace before or after the opening '<'
+ * - Parameters must be separated by a comma and whitespace
+ * - No whitespace after a parameter name
+ * - The list must not be empty
+ */
+    char_u *
+parse_generic_func_type_params(
+    char_u             *func_name,
+    char_u             *p,
+    gfargs_tab_T       *gfatab,
+    cctx_T             *cctx)
+{
+    // No white space allowed before the '<'
+    if (VIM_ISWHITE(*(p - 1)))
+    {
+       semsg(_(e_no_white_space_allowed_before_str_str), "<", p);
+       return NULL;
+    }
+
+    if (VIM_ISWHITE(*(p + 1)))
+    {
+       semsg(_(e_no_white_space_allowed_after_str_str), "<", p);
+       return NULL;
+    }
+
+    char_u         *start = ++p;
+
+    while (*p && *p != '>')
+    {
+       p = skipwhite(p);
+
+       if (*p == NUL || *p == '>')
+       {
+           semsg(_(e_missing_type_after_str), p - 1);
+           return NULL;
+       }
+
+       if (!ASCII_ISUPPER(*p))
+       {
+           if (ASCII_ISLOWER(*p))
+               semsg(_(e_type_var_name_must_start_with_uppercase_letter_str), p);
+           else
+               semsg(_(e_missing_type_after_str), p - 1);
+           return NULL;
+       }
+
+       char_u  *name_start = p;
+       char_u  *name_end = NULL;
+       char_u  cc;
+       size_t  name_len = 0;
+
+       p++;
+       while (ASCII_ISALNUM(*p) || *p == '_')
+           p++;
+       name_end = p;
+
+       name_len = name_end - name_start;
+       cc = *name_end;
+       *name_end = NUL;
+
+       int name_exists = generic_name_exists(name_start, name_len, gfatab,
+                                                                       cctx);
+       *name_end = cc;
+       if (name_exists)
+           return NULL;
+
+       if (ga_grow(&gfatab->gfat_param_types, 1) == FAIL)
+           return NULL;
+       type_T *gt =
+           &((type_T *)gfatab->gfat_param_types.ga_data)[gfatab->gfat_param_types.ga_len];
+       gfatab->gfat_param_types.ga_len++;
+
+       CLEAR_POINTER(gt);
+       gt->tt_type = VAR_ANY;
+       gt->tt_flags = TTFLAG_GENERIC;
+
+       if (ga_grow(&gfatab->gfat_args, 1) == FAIL)
+           return NULL;
+       generic_T *generic =
+           &((generic_T *)gfatab->gfat_args.ga_data)[gfatab->gfat_args.ga_len];
+       gfatab->gfat_args.ga_len++;
+
+       generic->gt_name = alloc(name_len + 1);
+       if (generic->gt_name == NULL)
+           return NULL;
+       vim_strncpy(generic->gt_name, name_start, name_len);
+       generic->gt_type = gt;
+
+       if (VIM_ISWHITE(*p))
+       {
+           semsg(_(e_no_white_space_allowed_after_str_str), generic->gt_name,
+                   name_start);
+           return NULL;
+       }
+
+       if (*p != ',' && *p != '>')
+       {
+           semsg(_(e_missing_comma_in_generic_function_str), start);
+           return NULL;
+       }
+       if (*p == ',')
+       {
+           if (!VIM_ISWHITE(*(p + 1)))
+           {
+               semsg(_(e_white_space_required_after_str_str), ",", p);
+               return NULL;
+           }
+           p++;
+       }
+    }
+    if (*p != '>')
+       return NULL;
+
+    if (generic_func_args_table_size(gfatab) == 0)
+    {
+       emsg_funcname(e_empty_type_list_for_generic_function_str, func_name);
+       return NULL;
+    }
+    p++;
+
+    return p;
+}
+
+/*
+ * Initialize a new generic function "fp" using the list of generic types and
+ * generic arguments in "gfatab".
+ *
+ * This function:
+ *   - Marks the function as generic.
+ *   - Sets the generic argument count and stores the type and argument lists.
+ *   - Transfers ownership of the arrays from the growarrays to the function.
+ *   - Initializes the generic function's lookup table.
+ */
+    void
+generic_func_init(ufunc_T *fp, gfargs_tab_T *gfatab)
+{
+    fp->uf_flags |= FC_GENERIC;
+    fp->uf_generic_argcount = gfatab->gfat_args.ga_len;
+    fp->uf_generic_args = (generic_T *)gfatab->gfat_args.ga_data;
+    ga_init(&gfatab->gfat_args);       // remove the reference to the args
+    fp->uf_generic_param_types = (type_T *)gfatab->gfat_param_types.ga_data;
+    ga_init(&gfatab->gfat_param_types);        // remove the reference to the types
+    ga_init(&fp->uf_generic_arg_types);
+    hash_init(&fp->uf_generic_functab);
+}
+
+/*
+ * Initialize the generic function args table
+ */
+    void
+generic_func_args_table_init(gfargs_tab_T *gfatab)
+{
+    ga_init2(&gfatab->gfat_args, sizeof(generic_T), 10);
+    ga_init2(&gfatab->gfat_param_types, sizeof(type_T), 10);
+    ga_init2(&gfatab->gfat_arg_types, sizeof(type_T), 10);
+}
+
+/*
+ * Return the number of entries in the generic function args table
+ */
+    int
+generic_func_args_table_size(gfargs_tab_T *gfatab)
+{
+    return gfatab->gfat_args.ga_len;
+}
+
+/*
+ * Free all the generic function args table items
+ */
+    void
+generic_func_args_table_clear(gfargs_tab_T *gfatab)
+{
+    clear_type_list(&gfatab->gfat_param_types);
+    clear_type_list(&gfatab->gfat_arg_types);
+    for (int i = 0; i < gfatab->gfat_args.ga_len; i++)
+    {
+       generic_T *generic = &((generic_T *)gfatab->gfat_args.ga_data)[i];
+       VIM_CLEAR(generic->gt_name);
+    }
+    ga_clear(&gfatab->gfat_args);
+}
+
+/*
+ * When a cloning a function "fp" to "new_fp", copy the generic function
+ * related information.
+ */
+    void
+copy_generic_function(ufunc_T *fp, ufunc_T *new_fp)
+{
+    int                i;
+    int                sz;
+
+    if (!IS_GENERIC_FUNC(fp))
+       return;
+
+    sz = fp->uf_generic_argcount * sizeof(type_T);
+    new_fp->uf_generic_param_types = alloc_clear(sz);
+    if (new_fp->uf_generic_param_types == NULL)
+       return;
+
+    memcpy(new_fp->uf_generic_param_types, fp->uf_generic_param_types, sz);
+
+    sz = fp->uf_generic_argcount * sizeof(generic_T);
+    new_fp->uf_generic_args = alloc_clear(sz);
+    if (new_fp->uf_generic_args == NULL)
+    {
+       VIM_CLEAR(new_fp->uf_generic_param_types);
+       return;
+    }
+    memcpy(new_fp->uf_generic_args, fp->uf_generic_args, sz);
+
+    for (i = 0; i < fp->uf_generic_argcount; i++)
+       new_fp->uf_generic_args[i].gt_name =
+           vim_strsave(fp->uf_generic_args[i].gt_name);
+
+    for (i = 0; i < fp->uf_generic_argcount; i++)
+       new_fp->uf_generic_args[i].gt_type =
+           &new_fp->uf_generic_param_types[i];
+
+    ga_init(&new_fp->uf_generic_arg_types);
+    hash_init(&new_fp->uf_generic_functab);
+}
+
+/*
+ * Returns the index of the generic type pointer "t" in the generic type list
+ * of the function "fp".
+ *
+ * Arguments:
+ *   fp - pointer to the generic function (ufunc_T)
+ *   t  - pointer to the type_T to search for in the function's generic type
+ *        list
+ *
+ * Returns:
+ *   The zero-based index of "t" in fp->uf_generic_param_types if found,
+ *   or -1 if not found.
+ */
+    static int
+get_generic_type_index(ufunc_T *fp, type_T *t)
+{
+    for (int i = 0; i < fp->uf_generic_argcount; i++)
+    {
+       if (&fp->uf_generic_param_types[i] == t)
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * Evaluates the type arguments for a generic function call and looks up the
+ * corresponding concrete function.
+ *
+ * Arguments:
+ *   ufunc - the original (possibly generic) function to evaluate
+ *   name  - the function name (used for error messages and lookup)
+ *   argp   - pointer to a pointer to the argument string; on entry, "*argp"
+ *            should point to the character after the function name (possibly
+ *            '<')
+ *
+ * Returns:
+ *   The concrete function corresponding to the given type arguments,
+ *   or NULL on error (with an error message reported).
+ *
+ * Behavior:
+ *   - If "ufunc" is a generic function and "*argp" points to '<', attempts to
+ *     find or instantiate the concrete function with the specified type
+ *     arguments.  On success, advances "*argp" past the type argument list.
+ *   - If "ufunc" is generic but "*argp" does not point to '<', reports a
+ *     missing type argument error.
+ *   - If "ufunc" is not generic but "*argp" points to '<', reports an error
+ *     that the function is not generic.
+ *   - Otherwise, returns the original function.
+ */
+    ufunc_T *
+eval_generic_func(
+    ufunc_T    *ufunc,
+    char_u     *name,
+    char_u     **argp)
+{
+    if (IS_GENERIC_FUNC(ufunc))
+    {
+       if (**argp == '<')
+           ufunc = find_generic_func(ufunc, name, argp);
+       else
+       {
+           emsg_funcname(e_generic_func_missing_type_args_str, name);
+           return NULL;
+       }
+    }
+    else if (**argp == '<')
+    {
+       emsg_funcname(e_not_a_generic_function_str, name);
+       return NULL;
+    }
+
+    return ufunc;
+}
+
+/*
+ * Checks if the string at "*argp" represents a generic function call with type
+ * arguments, i.e., if it starts with a '<', contains a valid type argument
+ * list, a closing '>', and is immediately followed by '('.
+ *
+ * On entry, "*argp" should point to the '<' character.
+ * If the pattern matches, advances "*argp" to point to the '(' and returns
+ * TRUE.  If not, leaves "*argp" unchanged and returns FALSE.
+ *
+ * Example:
+ *   "<number, string>("
+ */
+    int
+generic_func_call(char_u **argp)
+{
+    char_u     *p = *argp;
+
+    if (*p != '<')
+       return FALSE;
+
+    if (skip_generic_func_type_args(&p) == FAIL)
+       return FALSE;
+
+    if (*p != '(')
+       return FALSE;
+
+    *argp = p;
+    return TRUE;
+}
+
+/*
+ * Recursively replaces all occurrences of the generic type "generic_type" in a
+ * type structure with the corresponding concrete type from "new_ufunc", based
+ * on the mapping from the original generic function "ufunc".
+ *
+ * This is used when instantiating a new function "new_ufunc" from a generic
+ * function "ufunc" with specific type arguments. The function updates all
+ * relevant type pointers in place, including nested types (such as lists,
+ * dictionaries, and tuples).
+ *
+ * Arguments:
+ *   ufunc         - the original generic function
+ *   new_ufunc     - the new function being created with concrete types
+ *   generic_type  - the generic type to be replaced (may be a nested type)
+ *   specific_type - pointer to the location where the concrete type should be
+ *                  set
+ *   func_type     - pointer to the function type to update (may be NULL)
+ */
+    static void
+update_generic_type(
+    ufunc_T    *ufunc,
+    ufunc_T    *new_ufunc,
+    type_T     *generic_type,
+    type_T     **specific_type,
+    type_T     **func_type)
+{
+    int        idx;
+
+    switch (generic_type->tt_type)
+    {
+       case VAR_ANY:
+           idx = get_generic_type_index(ufunc, generic_type);
+           if (idx != -1)
+           {
+               *specific_type = new_ufunc->uf_generic_args[idx].gt_type;
+               if (func_type != NULL)
+                   *func_type = new_ufunc->uf_generic_args[idx].gt_type;
+           }
+           break;
+       case VAR_LIST:
+       case VAR_DICT:
+           update_generic_type(ufunc, new_ufunc, generic_type->tt_member,
+                   &(*specific_type)->tt_member,
+                   func_type != NULL ? &(*func_type)->tt_member : NULL);
+           break;
+       case VAR_TUPLE:
+           for (int i = 0; i < generic_type->tt_argcount; i++)
+               update_generic_type(ufunc, new_ufunc,
+                       generic_type->tt_args[i],
+                       &(*specific_type)->tt_args[i],
+                       func_type != NULL ? &(*func_type)->tt_args[i] : NULL);
+           break;
+       case VAR_FUNC:
+           for (int i = 0; i < generic_type->tt_argcount; i++)
+               update_generic_type(ufunc, new_ufunc,
+                       generic_type->tt_args[i],
+                       &(*specific_type)->tt_args[i],
+                       func_type != NULL ? &(*func_type)->tt_args[i] : NULL);
+           update_generic_type(ufunc, new_ufunc,
+                   generic_type->tt_member,
+                   &(*specific_type)->tt_member,
+                   func_type != NULL ? &(*func_type)->tt_member : NULL);
+           break;
+       default:
+           break;
+    }
+}
+
+/*
+ * Adds a new concrete instance of a generic function for a specific set of
+ * type arguments.
+ *
+ * Arguments:
+ *   fp       - the original generic function to instantiate
+ *   key      - a string key representing the specific type arguments (used for
+ *             lookup)
+ *   gfatab   - generic function args table containing the parsed type
+ *              arguments and their names
+ *
+ * Returns:
+ *   Pointer to the new ufunc_T representing the instantiated function,
+ *   or NULL if the function already exists or on allocation failure.
+ *
+ * This function:
+ *   - Checks if a function with the given type arguments already exists.
+ *   - Allocates and initializes a new function instance with the specific
+ *     types.
+ *   - Updates the function's name and expanded name to include the type
+ *     arguments.
+ *   - Copies and updates all relevant type information (argument types, return
+ *     type, vararg type, function type), replacing generic types with the
+ *     actual types.
+ *   - Sets the new function's status to UF_TO_BE_COMPILED.
+ *   - Registers the new function in the generic function's lookup table.
+ */
+    static ufunc_T *
+generic_func_add(ufunc_T *fp, char_u *key, gfargs_tab_T *gfatab)
+{
+    hashtab_T  *ht = &fp->uf_generic_functab;
+    long_u     hash;
+    hashitem_T *hi;
+    int                i;
+
+    hash = hash_hash(key);
+    hi = hash_lookup(ht, key, hash);
+    if (!HASHITEM_EMPTY(hi))
+       return NULL;
+
+    size_t     keylen = STRLEN(key);
+    gfitem_T    *gfitem = alloc(sizeof(gfitem_T) + keylen);
+    if (gfitem == NULL)
+       return NULL;
+
+    STRCPY(gfitem->gfi_name, key);
+
+    ufunc_T *new_fp = copy_function(fp, (int)(keylen + 2));
+    if (new_fp == NULL)
+    {
+       vim_free(gfitem);
+       return NULL;
+    }
+
+    new_fp->uf_generic_arg_types = gfatab->gfat_arg_types;
+    // now that the type arguments is copied, remove the reference to the type
+    // arguments
+    ga_init(&gfatab->gfat_arg_types);
+
+    if (fp->uf_class != NULL)
+       new_fp->uf_class = fp->uf_class;
+
+    // Create a new name for the function: name<type1, type2...>
+    new_fp->uf_name[new_fp->uf_namelen] =  '<';
+    STRCPY(new_fp->uf_name + new_fp->uf_namelen + 1, key);
+    new_fp->uf_name[new_fp->uf_namelen + keylen + 1] =  '>';
+    new_fp->uf_namelen += keylen + 2;
+
+    if (new_fp->uf_name_exp != NULL)
+    {
+       char_u  *new_name_exp = alloc(STRLEN(new_fp->uf_name_exp) + keylen + 3);
+       if (new_name_exp != NULL)
+       {
+           STRCPY(new_name_exp, new_fp->uf_name_exp);
+           STRCAT(new_name_exp, "<");
+           STRCAT(new_name_exp, key);
+           STRCAT(new_name_exp, ">");
+           vim_free(new_fp->uf_name_exp);
+           new_fp->uf_name_exp = new_name_exp;
+       }
+    }
+
+    gfitem->gfi_ufunc = new_fp;
+    gfitem->gfi_ufunc->uf_def_status = UF_TO_BE_COMPILED;
+
+    // create a copy of
+    // - all the argument types
+    // - return type
+    // - vararg type
+    // - function type
+    // if any generic type is used, it will be replaced below).
+    for (i = 0; i < fp->uf_args.ga_len; i++)
+       new_fp->uf_arg_types[i] = copy_type_deep(fp->uf_arg_types[i],
+                                               &new_fp->uf_type_list);
+
+    if (fp->uf_ret_type != NULL)
+       new_fp->uf_ret_type = copy_type_deep(fp->uf_ret_type,
+                                               &new_fp->uf_type_list);
+
+    if (fp->uf_va_type != NULL)
+       new_fp->uf_va_type = copy_type_deep(fp->uf_va_type,
+                                               &new_fp->uf_type_list);
+
+    if (fp->uf_func_type != NULL)
+       new_fp->uf_func_type = copy_type_deep(fp->uf_func_type,
+                                               &new_fp->uf_type_list);
+
+    // Replace the t_any generic types with the actual types
+    for (i = 0; i < fp->uf_generic_argcount; i++)
+    {
+       generic_T  *generic_arg;
+       generic_arg = (generic_T *)gfatab->gfat_args.ga_data + i;
+       generic_T *gt = &new_fp->uf_generic_args[i];
+       gt->gt_type = generic_arg->gt_type;
+    }
+
+    // Update any generic types in the function arguments
+    for (i = 0; i < fp->uf_args.ga_len; i++)
+       update_generic_type(fp, new_fp, fp->uf_arg_types[i],
+                           &new_fp->uf_arg_types[i],
+                           &new_fp->uf_func_type->tt_args[i]);
+
+    // Update the vararg type if it uses generic types
+    if (fp->uf_va_type != NULL)
+       update_generic_type(fp, new_fp, fp->uf_va_type, &new_fp->uf_va_type,
+                           NULL);
+
+    // Update the return type if it is a generic type
+    if (fp->uf_ret_type != NULL)
+       update_generic_type(fp, new_fp, fp->uf_ret_type, &new_fp->uf_ret_type,
+                           &new_fp->uf_func_type->tt_member);
+
+    hash_add_item(ht, hi, gfitem->gfi_name, hash);
+
+    return new_fp;
+}
+
+/*
+ * Looks up a concrete instance of a generic function "fp" using the type
+ * arguments specified in "gfatab".
+ *
+ * The lookup key is constructed by concatenating the type argument names from
+ * "gfatab", separated by ", ", and stored in the provided growarray
+ * "gfkey_gap".  The contents of "gfkey_gap" will be overwritten.
+ *
+ * Arguments:
+ *   fp        - the generic function to search in
+ *   gfatab    - generic function args table containing the parsed type
+ *               arguments and their names
+ *   gfkey_gap - growarray used to build and store the lookup key string
+ *
+ * Returns:
+ *   Pointer to the ufunc_T representing the concrete function if found, or
+ *   NULL if no matching function exists.
+ */
+    static ufunc_T *
+generic_lookup_func(ufunc_T *fp, gfargs_tab_T *gfatab, garray_T *gfkey_gap)
+{
+    hashtab_T  *ht = &fp->uf_generic_functab;
+    hashitem_T *hi;
+
+    for (int i = 0; i < gfatab->gfat_args.ga_len; i++)
+    {
+       generic_T  *generic_arg;
+
+       generic_arg = (generic_T *)gfatab->gfat_args.ga_data + i;
+       ga_concat(gfkey_gap, generic_arg->gt_name);
+
+       if (i != gfatab->gfat_args.ga_len - 1)
+       {
+           ga_append(gfkey_gap, ',');
+           ga_append(gfkey_gap, ' ');
+       }
+    }
+    ga_append(gfkey_gap, NUL);
+
+    char_u     *key = ((char_u *)gfkey_gap->ga_data);
+
+    hi = hash_find(ht, key);
+
+    if (HASHITEM_EMPTY(hi))
+       return NULL;
+
+    gfitem_T   *gfitem = HI2GFITEM(hi);
+    return gfitem->gfi_ufunc;
+}
+
+/*
+ * Returns a concrete instance of the generic function "fp" using the type
+ * arguments specified in "gfatab". If such an instance does not exist,
+ * it is created and registered.
+ *
+ * Arguments:
+ *   fp        - the generic function to instantiate
+ *   gfatab    - generic function args table containing the parsed type
+ *               arguments and their names
+ *
+ * Returns:
+ *   Pointer to the ufunc_T representing the concrete function instance,
+ *   or NULL if the type arguments are invalid or on allocation failure.
+ *
+ * Behavior:
+ *   - If "fp" is not a generic function and no type arguments are given,
+ *     returns "fp" as-is.
+ *   - If "fp" is not generic but type arguments are given, reports an error
+ *     and returns NULL.
+ *   - Validates the number of type arguments, reporting errors for missing,
+ *     too few, or too many.
+ *   - Looks up an existing function instance with the given types.
+ *   - If not found, creates and registers a new function instance.
+ */
+    ufunc_T *
+generic_func_get(ufunc_T *fp, gfargs_tab_T *gfatab)
+{
+    char       *emsg = NULL;
+
+    if (!IS_GENERIC_FUNC(fp))
+    {
+       if (gfatab && generic_func_args_table_size(gfatab) > 0)
+       {
+           emsg_funcname(e_not_a_generic_function_str, fp->uf_name);
+           return NULL;
+       }
+       return fp;
+    }
+
+    if (gfatab == NULL || gfatab->gfat_args.ga_len == 0)
+       emsg = e_generic_func_missing_type_args_str;
+    else if (gfatab->gfat_args.ga_len < fp->uf_generic_argcount)
+       emsg = e_not_enough_types_for_generic_function_str;
+    else if (gfatab->gfat_args.ga_len > fp->uf_generic_argcount)
+       emsg = e_too_many_types_for_generic_function_str;
+
+    if (emsg != NULL)
+    {
+       emsg_funcname(emsg, printable_func_name(fp));
+       return NULL;
+    }
+
+    // generic function call
+    garray_T gfkey_ga;
+
+    ga_init2(&gfkey_ga, 1, 80);
+
+    // Look up the function with specific types
+    ufunc_T    *generic_fp = generic_lookup_func(fp, gfatab, &gfkey_ga);
+    if (generic_fp == NULL)
+       // generic function with these type arguments doesn't exist.
+       // Create a new one.
+       generic_fp = generic_func_add(fp, (char_u *)gfkey_ga.ga_data, gfatab);
+    ga_clear(&gfkey_ga);
+
+    return generic_fp;
+}
+
+/*
+ * Looks up or creates a concrete instance of a generic function "ufunc" using
+ * the type arguments specified after the function name in "name".
+ *
+ * On entry, "name" points to the function name, and "*argp" points to the
+ * opening '<' of the type argument list (i.e., name + namelen).
+ *
+ * Arguments:
+ *   ufunc - the generic function to instantiate or look up
+ *   name  - the function name, followed by the type argument list
+ *   argp  - pointer to a pointer to the type argument list (should point to
+ *           '<'); on success, advanced to the character after the closing '>'
+ *
+ * Returns:
+ *   Pointer to the ufunc_T representing the concrete function instance if
+ *   successful, or NULL if parsing fails or the instance cannot be created.
+ *
+ * This function:
+ *   - Parses the type arguments from the string after the function name.
+ *   - Looks up an existing function instance with those type arguments.
+ *   - If not found, creates and registers a new function instance.
+ *   - Advances "*argp" to after the type argument list on success.
+ */
+    ufunc_T *
+find_generic_func(ufunc_T *ufunc, char_u *name, char_u **argp)
+{
+    gfargs_tab_T    gfatab;
+    char_u     *p;
+    ufunc_T    *new_ufunc = NULL;
+
+    generic_func_args_table_init(&gfatab);
+
+    // Get the list of types following the name
+    p = parse_generic_func_type_args(name, *argp - name, *argp, &gfatab, NULL);
+    if (p != NULL)
+    {
+       new_ufunc = generic_func_get(ufunc, &gfatab);
+       *argp = p;
+    }
+
+    generic_func_args_table_clear(&gfatab);
+
+    return new_ufunc;
+}
+
+/*
+ * Searches for a generic type with the given name "gt_name" in the generic
+ * function "ufunc".
+ *
+ * Arguments:
+ *   gt_name - the name of the generic type to search for
+ *   ufunc   - the generic function in which to search for the type
+ *
+ * Returns:
+ *   Pointer to the type_T representing the found generic type,
+ *   or NULL if the type is not found or if "ufunc" is not a generic function.
+ */
+    static type_T *
+find_generic_type_in_ufunc(char_u *gt_name, size_t name_len, ufunc_T *ufunc)
+{
+    if (!IS_GENERIC_FUNC(ufunc))
+       return NULL;
+
+    for (int i = 0; i < ufunc->uf_generic_argcount; i++)
+    {
+       generic_T *generic;
+
+       generic = ((generic_T *)ufunc->uf_generic_args) + i;
+       if (STRNCMP(generic->gt_name, gt_name, name_len) == 0)
+       {
+           type_T *type = generic->gt_type;
+           return type;
+       }
+    }
+
+    return NULL;
+}
+
+/*
+ * Searches for a generic type with the given name "gt_name" in the current
+ * function context "cctx" and its outer (enclosing) contexts, if necessary.
+ *
+ * Arguments:
+ *   gt_name - the name of the generic type to search for
+ *   cctx    - the current compile context, which may be nested
+ *
+ * Returns:
+ *   Pointer to the type_T representing the found generic type,
+ *   or NULL if the type is not found in the current or any outer context.
+ */
+    static type_T *
+find_generic_type_in_cctx(char_u *gt_name, size_t name_len, cctx_T *cctx)
+{
+    type_T     *type;
+
+    type = find_generic_type_in_ufunc(gt_name, name_len, cctx->ctx_ufunc);
+    if (type != NULL)
+       return type;
+
+    if (cctx->ctx_outer != NULL)
+       return find_generic_type_in_cctx(gt_name, name_len, cctx->ctx_outer);
+
+    return NULL;
+}
+
+/*
+ * Looks up a generic type with the given name "gt_name" in the generic
+ * function "ufunc".  If not found, searches in the enclosing compile context
+ * "cctx" (for nested functions).
+ *
+ * Arguments:
+ *   gt_name - the name of the generic type to search for
+ *   ufunc   - the generic function to search in first (may be NULL)
+ *   cctx    - the compile context to search in outer functions if not found
+ *             in "ufunc" (may be NULL)
+ *
+ * Returns:
+ *   Pointer to the type_T representing the found generic type, or NULL if the
+ *   type is not found in the given function or any outer context.
+ */
+    type_T *
+find_generic_type(
+    char_u     *gt_name,
+    size_t     name_len,
+    ufunc_T    *ufunc,
+    cctx_T     *cctx)
+{
+    if (ufunc != NULL)
+    {
+       type_T *type = find_generic_type_in_ufunc(gt_name, name_len, ufunc);
+       if (type != NULL)
+           return type;
+    }
+
+    if (cctx != NULL && ufunc != cctx->ctx_ufunc)
+       return find_generic_type_in_cctx(gt_name, name_len, cctx);
+
+    return NULL;
+}
+
+/*
+ * Frees all concrete function instances stored in the generic function table
+ * of "fp". This includes freeing each instantiated function and its
+ * associated gfitem_T structure, and clearing the hash table.
+ *
+ * Arguments:
+ *   fp - the generic function whose function table should be freed
+ */
+    static void
+free_generic_functab(ufunc_T *fp)
+{
+    hashtab_T  *ht = &fp->uf_generic_functab;
+    long       todo;
+    hashitem_T *hi;
+
+    todo = (long)ht->ht_used;
+    FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
+    {
+       if (!HASHITEM_EMPTY(hi))
+       {
+           gfitem_T    *gfitem = HI2GFITEM(hi);
+
+           func_clear_free(gfitem->gfi_ufunc, FALSE);
+           vim_free(gfitem);
+           --todo;
+       }
+    }
+    hash_clear(ht);
+}
+
+/*
+ * Frees all memory and state associated with a generic function "fp".
+ * This includes the generic type list, generic argument list, and all
+ * concrete function instances in the generic function table.
+ *
+ * Arguments:
+ *   fp - the generic function to clear
+ */
+    void
+generic_func_clear_items(ufunc_T *fp)
+{
+    VIM_CLEAR(fp->uf_generic_param_types);
+    clear_type_list(&fp->uf_generic_arg_types);
+    for (int i = 0; i < fp->uf_generic_argcount; i++)
+       VIM_CLEAR(fp->uf_generic_args[i].gt_name);
+    VIM_CLEAR(fp->uf_generic_args);
+    free_generic_functab(fp);
+    fp->uf_flags &= ~FC_GENERIC;
+}
+
+#endif // FEAT_EVAL
index d4593ea138087f6a2a3e05148eb0d5cb7d78d643..9a501dcbb9c518f26382c36f3948092bdd8d8d81 100644 (file)
@@ -204,6 +204,7 @@ may_generate_2STRING(int offset, int tostring_flags, cctx_T *cctx)
 
     RETURN_OK_IF_SKIP(cctx);
     type = get_type_on_stack(cctx, -1 - offset);
+
     switch (type->tt_type)
     {
        // nothing to be done
@@ -2125,6 +2126,7 @@ check_func_args_from_type(
 
     return OK;
 }
+
 /*
  * Generate an ISN_PCALL instruction.
  * "type" is the type of the FuncRef.
index 7e33770a041d7059a72612f6ecc357138fbd965e..ece4a3acf1849b2dc234ed6b0c7a652ce87ac23e 100644 (file)
@@ -846,7 +846,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
 
     // parse type, check for reserved name
     p = skipwhite(p + 1);
-    type = parse_type(&p, &si->sn_type_list, TRUE);
+    type = parse_type(&p, &si->sn_type_list, NULL, NULL, TRUE);
     if (type == NULL || check_reserved_name(name, FALSE) == FAIL)
     {
        vim_free(name);
index ab5e0775286f70cac179e4cf2d638c57a3461852..62e9344ef9ba06f2e428121434ff6bdf01cf154b 100644 (file)
@@ -101,7 +101,7 @@ copy_type_deep_rec(type_T *type, garray_T *type_gap, garray_T *seen_types)
  * Make a deep copy of "type".
  * When allocation fails returns "type".
  */
-    static type_T *
+    type_T *
 copy_type_deep(type_T *type, garray_T *type_gap)
 {
     garray_T seen_types;
@@ -342,7 +342,10 @@ get_list_type(type_T *member_type, garray_T *type_gap)
     type_T *type;
 
     // recognize commonly used types
-    if (member_type == NULL || member_type->tt_type == VAR_ANY)
+    // A generic type is t_any initially before being set to a concrete type
+    // later.  So don't use the static t_list_any for a generic type.
+    if (member_type == NULL || (member_type->tt_type == VAR_ANY
+                                       && !IS_GENERIC_TYPE(member_type)))
        return &t_list_any;
     if (member_type->tt_type == VAR_VOID
            || member_type->tt_type == VAR_UNKNOWN)
@@ -810,7 +813,29 @@ fp_typval2type(typval_T *tv, garray_T *type_gap)
                    type_gap);
        }
        else
-           ufunc = find_func(name, FALSE);
+       {
+           // Check if name contains "<".  If it does, then replace "<" with
+           // NUL and lookup the function and then look up the specific
+           // generic function using the supplied types.
+           char_u *p = generic_func_find_open_bracket(name);
+           if (p == NULL)
+               ufunc = find_func(name, FALSE);
+           else
+           {
+               // generic function
+               char_u  cc = *p;
+
+               *p = NUL;
+               ufunc = find_func(name, FALSE);
+               *p = cc;
+               if (ufunc != NULL && IS_GENERIC_FUNC(ufunc))
+               {
+                   ufunc = find_generic_func(ufunc, name, &p);
+                   if (ufunc == NULL)
+                       return NULL;
+               }
+           }
+       }
     }
     if (ufunc != NULL)
     {
@@ -1582,7 +1607,9 @@ parse_type_member(
        type_T      *type,
        garray_T    *type_gap,
        int         give_error,
-       char        *info)
+       char        *info,
+       ufunc_T     *ufunc,
+       cctx_T      *cctx)
 {
     char_u  *arg_start = *arg;
     type_T  *member_type;
@@ -1601,7 +1628,7 @@ parse_type_member(
     }
     *arg = skipwhite(*arg + 1);
 
-    member_type = parse_type(arg, type_gap, give_error);
+    member_type = parse_type(arg, type_gap, ufunc, cctx, give_error);
     if (member_type == NULL)
        return NULL;
 
@@ -1625,7 +1652,13 @@ parse_type_member(
  * Return NULL for failure.
  */
     static type_T *
-parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error)
+parse_type_func(
+    char_u     **arg,
+    size_t     len,
+    garray_T   *type_gap,
+    int                give_error,
+    ufunc_T    *ufunc,
+    cctx_T     *cctx)
 {
     char_u  *p;
     type_T  *type;
@@ -1665,7 +1698,7 @@ parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error)
                return NULL;
            }
 
-           type = parse_type(&p, type_gap, give_error);
+           type = parse_type(&p, type_gap, ufunc, cctx, give_error);
            if (type == NULL)
                return NULL;
            if ((flags & TTFLAG_VARARGS) != 0 && type->tt_type != VAR_LIST)
@@ -1725,7 +1758,7 @@ parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error)
        if (!VIM_ISWHITE(**arg) && give_error)
            semsg(_(e_white_space_required_after_str_str), ":", *arg - 1);
        *arg = skipwhite(*arg);
-       ret_type = parse_type(arg, type_gap, give_error);
+       ret_type = parse_type(arg, type_gap, ufunc, cctx, give_error);
        if (ret_type == NULL)
            return NULL;
     }
@@ -1755,7 +1788,12 @@ parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error)
  * Return NULL for failure.
  */
     static type_T *
-parse_type_tuple(char_u **arg, garray_T *type_gap, int give_error)
+parse_type_tuple(
+    char_u     **arg,
+    garray_T   *type_gap,
+    int                give_error,
+    ufunc_T    *ufunc,
+    cctx_T     *cctx)
 {
     char_u     *p;
     type_T     *type;
@@ -1792,7 +1830,7 @@ parse_type_tuple(char_u **arg, garray_T *type_gap, int give_error)
            p += 3;
        }
 
-       type = parse_type(&p, type_gap, give_error);
+       type = parse_type(&p, type_gap, ufunc, cctx, give_error);
        if (type == NULL)
            goto on_err;
 
@@ -1865,7 +1903,11 @@ on_err:
  * Return NULL for failure.
  */
     static type_T *
-parse_type_object(char_u **arg, garray_T *type_gap, int give_error)
+parse_type_object(
+    char_u     **arg,
+    garray_T   *type_gap,
+    int                give_error,
+    cctx_T     *cctx)
 {
     char_u     *arg_start = *arg;
     type_T     *object_type;
@@ -1889,7 +1931,7 @@ parse_type_object(char_u **arg, garray_T *type_gap, int give_error)
     // skip spaces following "object<"
     *arg = skipwhite(*arg + 1);
 
-    object_type = parse_type(arg, type_gap, give_error);
+    object_type = parse_type(arg, type_gap, NULL, cctx, give_error);
     if (object_type == NULL)
        return NULL;
 
@@ -1926,7 +1968,9 @@ parse_type_user_defined(
     char_u     **arg,
     size_t     len,
     garray_T   *type_gap,
-    int                give_error)
+    int                give_error,
+    ufunc_T    *ufunc,
+    cctx_T     *cctx)
 {
     int                did_emsg_before = did_emsg;
     typval_T   tv;
@@ -1968,6 +2012,14 @@ parse_type_user_defined(
        clear_tv(&tv);
     }
 
+    // Check whether it is a generic type
+    type_T *type = find_generic_type(*arg, len, ufunc, cctx);
+    if (type != NULL)
+    {
+       *arg += len;
+       return type;
+    }
+
     if (give_error && (did_emsg == did_emsg_before))
     {
        char_u  *p = skip_type(*arg, FALSE);
@@ -1987,7 +2039,12 @@ parse_type_user_defined(
  * Return NULL for failure.
  */
     type_T *
-parse_type(char_u **arg, garray_T *type_gap, int give_error)
+parse_type(
+    char_u     **arg,
+    garray_T   *type_gap,
+    ufunc_T    *ufunc,
+    cctx_T     *cctx,
+    int                give_error)
 {
     char_u  *p = *arg;
     size_t  len;
@@ -2029,8 +2086,9 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
            if (len == 4 && STRNCMP(*arg, "dict", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_dict_any,
-                                                type_gap, give_error, "dict");
+               return parse_type_member(arg, &t_dict_any, type_gap,
+                                               give_error, "dict", ufunc,
+                                               cctx);
            }
            break;
        case 'f':
@@ -2040,7 +2098,8 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
                return &t_float;
            }
            if (len == 4 && STRNCMP(*arg, "func", len) == 0)
-               return parse_type_func(arg, len, type_gap, give_error);
+               return parse_type_func(arg, len, type_gap, give_error, ufunc,
+                                                                       cctx);
            break;
        case 'j':
            if (len == 3 && STRNCMP(*arg, "job", len) == 0)
@@ -2053,8 +2112,9 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
            if (len == 4 && STRNCMP(*arg, "list", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_list_any,
-                                                type_gap, give_error, "list");
+               return parse_type_member(arg, &t_list_any, type_gap,
+                                               give_error, "list", ufunc,
+                                               cctx);
            }
            break;
        case 'n':
@@ -2068,7 +2128,7 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
            if (len == 6 && STRNCMP(*arg, "object", len) == 0)
            {
                *arg += len;
-               return parse_type_object(arg, type_gap, give_error);
+               return parse_type_object(arg, type_gap, give_error, cctx);
            }
            break;
        case 's':
@@ -2082,7 +2142,8 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
            if (len == 5 && STRNCMP(*arg, "tuple", len) == 0)
            {
                *arg += len;
-               return parse_type_tuple(arg, type_gap, give_error);
+               return parse_type_tuple(arg, type_gap, give_error, ufunc,
+                                                                       cctx);
            }
            break;
        case 'v':
@@ -2095,7 +2156,8 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
     }
 
     // User defined type
-    return parse_type_user_defined(arg, len, type_gap, give_error);
+    return parse_type_user_defined(arg, len, type_gap, give_error, ufunc,
+                                                                       cctx);
 }
 
 /*