src/vim9compile.c \
src/vim9execute.c \
src/vim9expr.c \
+ src/vim9generics.c \
src/vim9instr.c \
src/vim9script.c \
src/vim9type.c \
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 \
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*
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*
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*
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*
-*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
- 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?
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|.
-*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
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|
==============================================================================
==============================================================================
-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
==============================================================================
-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
==============================================================================
-9. Rationale *vim9-rationale*
+8. Rationale *vim9-rationale*
The :def command ~
vim9compile.c \
vim9execute.c \
vim9expr.c \
+ vim9generics.c \
vim9instr.c \
vim9script.c \
vim9type.c \
$(OUTDIR)/vim9compile.o \
$(OUTDIR)/vim9execute.o \
$(OUTDIR)/vim9expr.o \
+ $(OUTDIR)/vim9generics.o \
$(OUTDIR)/vim9instr.o \
$(OUTDIR)/vim9script.o \
$(OUTDIR)/vim9type.o \
$(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
$(OUTDIR)\vim9compile.obj \
$(OUTDIR)\vim9execute.obj \
$(OUTDIR)\vim9expr.obj \
+ $(OUTDIR)\vim9generics.obj \
$(OUTDIR)\vim9instr.obj \
$(OUTDIR)\vim9script.obj \
$(OUTDIR)\vim9type.obj \
$(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
proto/vim9compile.pro \
proto/vim9execute.pro \
proto/vim9expr.pro \
+ proto/vim9generics.pro \
proto/vim9instr.pro \
proto/vim9script.pro \
proto/vim9type.pro \
vim9compile.c \
vim9execute.c \
vim9expr.c \
+ vim9generics.c \
vim9instr.c \
vim9script.c \
vim9type.c \
vim9compile.obj \
vim9execute.obj \
vim9expr.obj \
+ vim9generics.obj \
vim9instr.obj \
vim9script.obj \
vim9type.obj \
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 \
vim9compile.c \
vim9execute.c \
vim9expr.c \
+ vim9generics.c \
vim9instr.c \
vim9script.c \
vim9type.c \
objects/vim9compile.o \
objects/vim9execute.o \
objects/vim9expr.o \
+ objects/vim9generics.o \
objects/vim9instr.o \
objects/vim9script.o \
objects/vim9type.o \
vim9compile.pro \
vim9execute.pro \
vim9expr.pro \
+ vim9generics.pro \
vim9instr.pro \
vim9script.pro \
vim9type.pro \
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
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 \
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[]
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
* 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.
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;
break;
}
}
+
+ return OK;
}
/*
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,
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;
}
}
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;
}
// 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;
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);
{
++*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);
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;
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);
*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
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;
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:
if (**arg == '{')
{
// ->{lambda}()
- ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
+ ret = get_lambda_tv(arg, rettv, FALSE, evalarg, NULL);
}
else
{
;
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
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;
}
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;
}
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
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;
}
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);
}
{
// 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:
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.
{
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)
// "&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 (
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"
"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 ""
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
# include "vim9compile.pro"
# include "vim9execute.pro"
# include "vim9expr.pro"
+# include "vim9generics.pro"
# include "vim9instr.pro"
# include "vim9type.pro"
# endif
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);
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);
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);
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);
--- /dev/null
+/* 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 : */
/* 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);
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);
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;
#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
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);
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
#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)
#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
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;
/*
test_vim9_expr \
test_vim9_fails \
test_vim9_func \
+ test_vim9_generics \
test_vim9_import \
test_vim9_python3 \
test_vim9_script \
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 \
--- /dev/null
+" 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
*/
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;
}
}
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;
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),
}
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;
{
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;
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;
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)
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);
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);
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;
}
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;
{
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]);
}
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;
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;
++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;
}
++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
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)
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);
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)
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);
vim_free(tofree);
vim_free(name);
+ generic_func_args_table_clear(&gfatab);
return ret;
}
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;
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;
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:
eap->arg);
return NULL;
}
+
p = eap->arg;
}
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.
*/
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;
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;
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;
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);
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);
}
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]))
{
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)
{
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;
* 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;
// 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);
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);
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1577,
/**/
1576,
/**/
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;
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.
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;
}
}
}
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);
// 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.
p = skipwhite(eni_name_end);
char_u *init_expr = NULL;
- if (*p == '(')
+ if (*p == '(' || *p == '<')
{
if (VIM_ISWHITE(p[-1]))
{
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)
// enddef
// static def ClassFunction()
// enddef
- // TODO:
- // def <Tval> someMethod()
+ // def someMethod<typeA, typeB>()
// enddef
else if (checkforcmd(&p, "def", 3))
{
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)
}
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;
char_u *name_end,
evalarg_T *evalarg,
char_u **arg,
+ gfargs_tab_T *gfatab, // generic types
typval_T *rettv)
{
ufunc_T *fp;
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)
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.
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);
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
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
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;
}
/*
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;
#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;
}
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;
}
compiletype_T compile_type;
int funcref_isn_idx = -1;
lvar_T *lvar = NULL;
+ char_u *bracket_start = NULL;
if (eap->forceit)
{
++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))
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)
{
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;
}
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;
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)
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;
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;
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]))
{
}
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;
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
{
status = generate_GET_OBJ_MEMBER(cctx, m_idx,
ocm->ocm_type);
if (status == FAIL)
- return FAIL;
+ goto done;
}
}
// 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
&& 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.
*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)
{
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".
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);
{
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".
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;
}
/*
{
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;
}
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;
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)
int
compile_load(
char_u **arg,
+ size_t namelen,
char_u *end_arg,
cctx_T *cctx,
int is_expr,
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) == ':')
{
// load namespaced variable
name = vim_strnsave(*arg + 2, end - (*arg + 2));
if (name == NULL)
- return FAIL;
+ goto theend;
switch (**arg)
{
{
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);
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;
}
vim_free(name);
name = vim_strnsave(*arg, end - *arg);
if (name == NULL)
- return FAIL;
+ goto theend;
}
break;
case 'w': isn_type = ISN_LOADW; break;
}
else
{
- size_t len = end - *arg;
int idx;
int method_idx;
int gen_load = FALSE;
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))
{
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)
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;
}
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
// "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);
// 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)
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;
}
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;
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;
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;
}
// 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);
emsg_funcname(e_unknown_function_str, namebuf);
theend:
+ generic_func_args_table_clear(&gfatab);
vim_free(tofree);
return res;
}
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);
// 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
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;
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;
--- /dev/null
+/* 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
RETURN_OK_IF_SKIP(cctx);
type = get_type_on_stack(cctx, -1 - offset);
+
switch (type->tt_type)
{
// nothing to be done
return OK;
}
+
/*
* Generate an ISN_PCALL instruction.
* "type" is the type of the FuncRef.
// 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);
* 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;
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)
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)
{
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;
}
*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;
* 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;
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)
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;
}
* 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;
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;
* 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;
// 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;
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;
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);
* 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;
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':
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)
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':
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':
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':
}
// 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);
}
/*