To: vim_dev@googlegroups.com Subject: Patch 8.2.0512 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0512 Problem: Vim9: no optional arguments in func type. Solution: Check for question mark after type. Find function reference without function(). Files: src/vim9compile.c, src/vim9execute.c, src/structs.h, src/globals.h, src/vim.h, src/vim9.h, src/userfunc.c, src/testdir/Make_all.mak, src/testdir/test_vim9_script.vim, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.0511/src/vim9compile.c 2020-04-03 21:59:29.329665634 +0200 --- src/vim9compile.c 2020-04-05 17:05:23.371164554 +0200 *************** *** 120,126 **** scope_T *ctx_scope; // current scope, NULL at toplevel garray_T ctx_type_stack; // type of each item on the stack ! garray_T *ctx_type_list; // space for adding types }; static char e_var_notfound[] = N_("E1001: variable not found: %s"); --- 120,126 ---- scope_T *ctx_scope; // current scope, NULL at toplevel garray_T ctx_type_stack; // type of each item on the stack ! garray_T *ctx_type_list; // list of pointers to allocated types }; static char e_var_notfound[] = N_("E1001: variable not found: %s"); *************** *** 223,228 **** --- 223,248 ---- return OK; } + /* + * Allocate memory for a type_T and add the pointer to type_gap, so that it can + * be freed later. + */ + static type_T * + alloc_type(garray_T *type_gap) + { + type_T *type; + + if (ga_grow(type_gap, 1) == FAIL) + return NULL; + type = ALLOC_CLEAR_ONE(type_T); + if (type != NULL) + { + ((type_T **)type_gap->ga_data)[type_gap->ga_len] = type; + ++type_gap->ga_len; + } + return type; + } + static type_T * get_list_type(type_T *member_type, garray_T *type_gap) { *************** *** 241,250 **** return &t_list_string; // Not a common type, create a new entry. ! if (ga_grow(type_gap, 1) == FAIL) return &t_any; - type = ((type_T *)type_gap->ga_data) + type_gap->ga_len; - ++type_gap->ga_len; type->tt_type = VAR_LIST; type->tt_member = member_type; type->tt_argcount = 0; --- 261,269 ---- return &t_list_string; // Not a common type, create a new entry. ! type = alloc_type(type_gap); ! if (type == NULL) return &t_any; type->tt_type = VAR_LIST; type->tt_member = member_type; type->tt_argcount = 0; *************** *** 270,279 **** return &t_dict_string; // Not a common type, create a new entry. ! if (ga_grow(type_gap, 1) == FAIL) return &t_any; - type = ((type_T *)type_gap->ga_data) + type_gap->ga_len; - ++type_gap->ga_len; type->tt_type = VAR_DICT; type->tt_member = member_type; type->tt_argcount = 0; --- 289,297 ---- return &t_dict_string; // Not a common type, create a new entry. ! type = alloc_type(type_gap); ! if (type == NULL) return &t_any; type->tt_type = VAR_DICT; type->tt_member = member_type; type->tt_argcount = 0; *************** *** 325,334 **** } // Not a common type or has arguments, create a new entry. ! if (ga_grow(type_gap, 1) == FAIL) return &t_any; - type = ((type_T *)type_gap->ga_data) + type_gap->ga_len; - ++type_gap->ga_len; type->tt_type = VAR_FUNC; type->tt_member = ret_type; type->tt_args = NULL; --- 343,351 ---- } // Not a common type or has arguments, create a new entry. ! type = alloc_type(type_gap); ! if (type == NULL) return &t_any; type->tt_type = VAR_FUNC; type->tt_member = ret_type; type->tt_args = NULL; *************** *** 336,341 **** --- 353,381 ---- } /* + * For a function type, reserve space for "argcount" argument types. + */ + static int + func_type_add_arg_types( + type_T *functype, + int argcount, + int min_argcount, + garray_T *type_gap) + { + if (ga_grow(type_gap, 1) == FAIL) + return FAIL; + functype->tt_args = ALLOC_CLEAR_MULT(type_T *, argcount); + if (functype->tt_args == NULL) + return FAIL; + ((type_T **)type_gap->ga_data)[type_gap->ga_len] = (void *)functype->tt_args; + ++type_gap->ga_len; + + functype->tt_argcount = argcount; + functype->tt_min_argcount = min_argcount; + return OK; + } + + /* * Return the type_T for a typval. Only for primitive types. */ static type_T * *************** *** 810,821 **** * Consumes "name". */ static int ! generate_PUSHFUNC(cctx_T *cctx, char_u *name) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, &t_func_void)) == NULL) return FAIL; isn->isn_arg.string = name; --- 850,861 ---- * Consumes "name". */ static int ! generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, type)) == NULL) return FAIL; isn->isn_arg.string = name; *************** *** 1010,1016 **** // drop the value types stack->ga_len -= count; ! // Use the first value type for the list member type. Use "void" for an // empty list. if (count > 0) member = ((type_T **)stack->ga_data)[stack->ga_len]; --- 1050,1056 ---- // drop the value types stack->ga_len -= count; ! // Use the first value type for the list member type. Use "any" for an // empty list. if (count > 0) member = ((type_T **)stack->ga_data)[stack->ga_len]; *************** *** 1533,1554 **** *arg += len; return &t_float; #else ! emsg(_("E1055: This Vim is not compiled with float support")); return &t_any; #endif } if (len == 4 && STRNCMP(*arg, "func", len) == 0) { type_T *type; ! type_T *ret_type = &t_void; int argcount = -1; int flags = 0; type_T *arg_type[MAX_FUNC_ARGS + 1]; // func({type}, ...): {type} *arg += len; if (**arg == '(') { p = ++*arg; argcount = 0; while (*p != NUL && *p != ')') --- 1573,1599 ---- *arg += len; return &t_float; #else ! emsg(_("E1076: This Vim is not compiled with float support")); return &t_any; #endif } if (len == 4 && STRNCMP(*arg, "func", len) == 0) { type_T *type; ! type_T *ret_type = &t_any; int argcount = -1; int flags = 0; + int first_optional = -1; type_T *arg_type[MAX_FUNC_ARGS + 1]; // func({type}, ...): {type} *arg += len; if (**arg == '(') { + // "func" may or may not return a value, "func()" does + // not return a value. + ret_type = &t_void; + p = ++*arg; argcount = 0; while (*p != NUL && *p != ')') *************** *** 1560,1565 **** --- 1605,1622 ---- } arg_type[argcount++] = parse_type(&p, type_gap); + if (*p == '?') + { + if (first_optional == -1) + first_optional = argcount; + ++p; + } + else if (first_optional != -1) + { + emsg(_("E1007: mandatory argument after optional argument")); + return &t_any; + } + if (*p != ',' && *skipwhite(p) == ',') { semsg(_(e_no_white_before), ","); *************** *** 1596,1619 **** *arg = skipwhite(*arg); ret_type = parse_type(arg, type_gap); } ! type = get_func_type(ret_type, flags == 0 ? argcount : 99, type_gap); if (flags != 0) type->tt_flags = flags; if (argcount > 0) { ! int type_ptr_cnt = (sizeof(type_T *) * argcount ! + sizeof(type_T) - 1) / sizeof(type_T); ! ! type->tt_argcount = argcount; ! // Get space from "type_gap" to avoid having to keep track ! // of the pointer and freeing it. ! ga_grow(type_gap, type_ptr_cnt); ! if (ga_grow(type_gap, type_ptr_cnt) == FAIL) return &t_any; - type->tt_args = - ((type_T **)type_gap->ga_data) + type_gap->ga_len; - type_gap->ga_len += type_ptr_cnt; mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); } --- 1653,1669 ---- *arg = skipwhite(*arg); ret_type = parse_type(arg, type_gap); } ! type = get_func_type(ret_type, ! flags == 0 && first_optional == -1 ? argcount : 99, type_gap); if (flags != 0) type->tt_flags = flags; if (argcount > 0) { ! if (func_type_add_arg_types(type, argcount, ! first_optional == -1 ? argcount : first_optional, ! type_gap) == FAIL) return &t_any; mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); } *************** *** 1775,1781 **** return *tofree; } } ! // TODO: function and partial argument types return name; } --- 1825,1886 ---- return *tofree; } } ! if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL) ! { ! garray_T ga; ! int i; ! ! ga_init2(&ga, 1, 100); ! if (ga_grow(&ga, 20) == FAIL) ! return "[unknown]"; ! *tofree = ga.ga_data; ! STRCPY(ga.ga_data, "func("); ! ga.ga_len += 5; ! ! for (i = 0; i < type->tt_argcount; ++i) ! { ! char *arg_free; ! char *arg_type = type_name(type->tt_args[i], &arg_free); ! int len; ! ! if (i > 0) ! { ! STRCPY(ga.ga_data + ga.ga_len, ", "); ! ga.ga_len += 2; ! } ! len = (int)STRLEN(arg_type); ! if (ga_grow(&ga, len + 6) == FAIL) ! { ! vim_free(arg_free); ! return "[unknown]"; ! } ! *tofree = ga.ga_data; ! STRCPY(ga.ga_data + ga.ga_len, arg_type); ! ga.ga_len += len; ! vim_free(arg_free); ! } ! ! if (type->tt_member == &t_void) ! STRCPY(ga.ga_data + ga.ga_len, ")"); ! else ! { ! char *ret_free; ! char *ret_name = type_name(type->tt_member, &ret_free); ! int len; ! ! len = (int)STRLEN(ret_name) + 4; ! if (ga_grow(&ga, len) == FAIL) ! { ! vim_free(ret_free); ! return "[unknown]"; ! } ! *tofree = ga.ga_data; ! STRCPY(ga.ga_data + ga.ga_len, "): "); ! STRCPY(ga.ga_data + ga.ga_len + 3, ret_name); ! vim_free(ret_free); ! } ! return ga.ga_data; ! } return name; } *************** *** 1868,1874 **** } /* ! * Generate an instruction to load script-local variable "name". */ static int compile_load_scriptvar( --- 1973,1981 ---- } /* ! * Generate an instruction to load script-local variable "name", without the ! * leading "s:". ! * Also finds imported variables. */ static int compile_load_scriptvar( *************** *** 1933,1939 **** } else { ! // TODO: check this is a variable, not a function generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT, import->imp_sid, import->imp_var_vals_idx, --- 2040,2046 ---- } else { ! // TODO: check this is a variable, not a function? generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT, import->imp_sid, import->imp_var_vals_idx, *************** *** 1947,1952 **** --- 2054,2070 ---- return FAIL; } + static int + generate_funcref(cctx_T *cctx, char_u *name) + { + ufunc_T *ufunc = find_func(name, cctx); + + if (ufunc == NULL) + return FAIL; + + return generate_PUSHFUNC(cctx, vim_strsave(name), ufunc->uf_func_type); + } + /* * Compile a variable name into a load instruction. * "end" points to just after the name. *************** *** 2051,2060 **** || (len == 5 && STRNCMP("false", *arg, 5) == 0)) res = generate_PUSHBOOL(cctx, **arg == 't' ? VVAL_TRUE : VVAL_FALSE); ! else if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version ! == SCRIPT_VERSION_VIM9) ! // in Vim9 script "var" can be script-local. ! res = compile_load_scriptvar(cctx, name, *arg, &end, error); } } if (gen_load) --- 2169,2189 ---- || (len == 5 && STRNCMP("false", *arg, 5) == 0)) res = generate_PUSHBOOL(cctx, **arg == 't' ? VVAL_TRUE : VVAL_FALSE); ! else ! { ! // "var" can be script-local even without using "s:" if it ! // already exists. ! if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version ! == SCRIPT_VERSION_VIM9 ! || lookup_script(*arg, len) == OK) ! res = compile_load_scriptvar(cctx, name, *arg, &end, ! FALSE); ! ! // When the name starts with an uppercase letter or "x:" it ! // can be a user defined function. ! if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':')) ! res = generate_funcref(cctx, name); ! } } } if (gen_load) *************** *** 2149,2159 **** // builtin function idx = find_internal_func(name); if (idx >= 0) - { res = generate_BCALL(cctx, idx, argcount); ! goto theend; ! } ! semsg(_(e_unknownfunc), namebuf); } // If we can find the function by name generate the right call. --- 2278,2287 ---- // builtin function idx = find_internal_func(name); if (idx >= 0) res = generate_BCALL(cctx, idx, argcount); ! else ! semsg(_(e_unknownfunc), namebuf); ! goto theend; } // If we can find the function by name generate the right call. *************** *** 2264,2269 **** --- 2392,2399 ---- static int check_type(type_T *expected, type_T *actual, int give_msg) { + int ret = OK; + if (expected->tt_type != VAR_UNKNOWN) { if (expected->tt_type != actual->tt_type) *************** *** 2274,2290 **** } if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST) { - int ret; - // void is used for an empty list or dict ! if (actual->tt_member == &t_void) ! ret = OK; ! else ret = check_type(expected->tt_member, actual->tt_member, FALSE); - if (ret == FAIL && give_msg) - type_mismatch(expected, actual); - return ret; } } return OK; } --- 2404,2424 ---- } if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST) { // void is used for an empty list or dict ! if (actual->tt_member != &t_void) ret = check_type(expected->tt_member, actual->tt_member, FALSE); } + else if (expected->tt_type == VAR_FUNC) + { + if (expected->tt_member != &t_any) + ret = check_type(expected->tt_member, actual->tt_member, FALSE); + if (ret == OK && expected->tt_argcount != -1 + && (actual->tt_argcount < expected->tt_min_argcount + || actual->tt_argcount > expected->tt_argcount)) + ret = FAIL; + } + if (ret == FAIL && give_msg) + type_mismatch(expected, actual); } return OK; } *************** *** 2357,2362 **** --- 2491,2497 ---- ufunc = rettv.vval.v_partial->pt_func; ++ufunc->uf_refcount; clear_tv(&rettv); + ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10); // The function will have one line: "return {expr}". // Compile it into instructions. *************** *** 2401,2406 **** --- 2536,2542 ---- ufunc = rettv.vval.v_partial->pt_func; ++ufunc->uf_refcount; clear_tv(&rettv); + ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10); // The function will have one line: "return {expr}". // Compile it into instructions. *************** *** 3679,3685 **** type = &t_string; if (is_decl) { ! semsg(_("E1065: Cannot declare an environment variable: %s"), name); goto theend; } } --- 3815,3822 ---- type = &t_string; if (is_decl) { ! semsg(_("E1065: Cannot declare an environment variable: %s"), ! name); goto theend; } } *************** *** 3761,3767 **** if (is_decl) { semsg(_("E1054: Variable already declared in the script: %s"), ! name); goto theend; } } --- 3898,3904 ---- if (is_decl) { semsg(_("E1054: Variable already declared in the script: %s"), ! name); goto theend; } } *************** *** 3803,3819 **** goto theend; } - // +=, /=, etc. require an existing variable if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE) { if (oplen > 1 && !heredoc) { semsg(_("E1020: cannot use an operator on a new variable: %s"), name); goto theend; } // new local variable idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type); if (idx < 0) goto theend; --- 3940,3959 ---- goto theend; } if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE) { if (oplen > 1 && !heredoc) { + // +=, /=, etc. require an existing variable semsg(_("E1020: cannot use an operator on a new variable: %s"), name); goto theend; } // new local variable + if ((type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL) + && var_check_func_name(name, TRUE)) + goto theend; idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type); if (idx < 0) goto theend; *************** *** 3897,3903 **** if (idx >= 0 && (is_decl || !has_type)) { lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; ! if (!has_type) { if (stacktype->tt_type == VAR_VOID) { --- 4037,4043 ---- if (idx >= 0 && (is_decl || !has_type)) { lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; ! if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) { *************** *** 3905,3915 **** goto theend; } else ! lvar->lv_type = stacktype; } ! else ! if (check_type(lvar->lv_type, stacktype, TRUE) == FAIL) ! goto theend; } else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL) goto theend; --- 4045,4063 ---- goto theend; } else ! { ! // An empty list or dict has a &t_void member, for a ! // variable that implies &t_any. ! if (stacktype == &t_list_empty) ! lvar->lv_type = &t_list_any; ! else if (stacktype == &t_dict_empty) ! lvar->lv_type = &t_dict_any; ! else ! lvar->lv_type = stacktype; ! } } ! else if (check_type(lvar->lv_type, stacktype, TRUE) == FAIL) ! goto theend; } else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL) goto theend; *************** *** 3946,3952 **** generate_PUSHBLOB(cctx, NULL); break; case VAR_FUNC: ! generate_PUSHFUNC(cctx, NULL); break; case VAR_PARTIAL: generate_PUSHPARTIAL(cctx, NULL); --- 4094,4100 ---- generate_PUSHBLOB(cctx, NULL); break; case VAR_FUNC: ! generate_PUSHFUNC(cctx, NULL, &t_func_void); break; case VAR_PARTIAL: generate_PUSHPARTIAL(cctx, NULL); *************** *** 4039,4045 **** idx = get_script_item_idx(sid, rawname, TRUE); // TODO: specific type if (idx < 0) ! generate_OLDSCRIPT(cctx, ISN_STORES, name, sid, &t_any); else generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, sid, idx, &t_any); --- 4187,4210 ---- idx = get_script_item_idx(sid, rawname, TRUE); // TODO: specific type if (idx < 0) ! { ! char_u *name_s = name; ! ! // Include s: in the name for store_var() ! if (name[1] != ':') ! { ! int len = (int)STRLEN(name) + 3; ! ! name_s = alloc(len); ! if (name_s == NULL) ! name_s = name; ! else ! vim_snprintf((char *)name_s, len, "s:%s", name); ! } ! generate_OLDSCRIPT(cctx, ISN_STORES, name_s, sid, &t_any); ! if (name_s != name) ! vim_free(name_s); ! } else generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, sid, idx, &t_any); *************** *** 5357,5369 **** { int is_ex_command; if (line != NULL && *line == '|') // the line continues after a '|' ++line; else if (line != NULL && *line != NUL) { ! if (emsg_before == called_emsg) ! semsg(_("E488: Trailing characters: %s"), line); goto erret; } else --- 5522,5538 ---- { int is_ex_command; + // Bail out on the first error to avoid a flood of errors and report + // the right line number when inside try/catch. + if (emsg_before != called_emsg) + goto erret; + if (line != NULL && *line == '|') // the line continues after a '|' ++line; else if (line != NULL && *line != NUL) { ! semsg(_("E488: Trailing characters: %s"), line); goto erret; } else *************** *** 5653,5658 **** --- 5822,5858 ---- dfunc->df_varcount = cctx.ctx_max_local; } + { + int argcount = ufunc->uf_args.ga_len + + (ufunc->uf_va_name == NULL ? 0 : 1); + + // Create a type for the function, with the return type and any + // argument types. + ufunc->uf_func_type = get_func_type(ufunc->uf_ret_type, argcount, + &ufunc->uf_type_list); + if (argcount > 0) + { + if (func_type_add_arg_types(ufunc->uf_func_type, argcount, + argcount - ufunc->uf_def_args.ga_len, + &ufunc->uf_type_list) == FAIL) + { + ret = FAIL; + goto erret; + } + if (ufunc->uf_arg_types == NULL) + { + int i; + + // lambda does not have argument types. + for (i = 0; i < argcount; ++i) + ufunc->uf_func_type->tt_args[i] = &t_any; + } + else + mch_memmove(ufunc->uf_func_type->tt_args, + ufunc->uf_arg_types, sizeof(type_T *) * argcount); + } + } + ret = OK; erret: *** ../vim-8.2.0511/src/vim9execute.c 2020-04-01 22:10:56.428201257 +0200 --- src/vim9execute.c 2020-04-05 16:52:32.150189604 +0200 *************** *** 377,383 **** funccal_entry_T entry; save_funccal(&entry); ! set_var_const(name, NULL, tv, FALSE, 0); restore_funccal(); } --- 377,383 ---- funccal_entry_T entry; save_funccal(&entry); ! set_var_const(name, NULL, tv, FALSE, LET_NO_COMMAND); restore_funccal(); } *************** *** 739,745 **** --ectx.ec_stack.ga_len; if (di == NULL) ! store_var(iptr->isn_arg.string, STACK_TV_BOT(0)); else { clear_tv(&di->di_tv); --- 739,745 ---- --ectx.ec_stack.ga_len; if (di == NULL) ! store_var(name, STACK_TV_BOT(0)); else { clear_tv(&di->di_tv); *************** *** 1828,1834 **** iptr->isn_arg.loadstore.ls_sid); smsg("%4d LOADS s:%s from %s", current, ! iptr->isn_arg.string, si->sn_name); } break; case ISN_LOADG: --- 1828,1834 ---- iptr->isn_arg.loadstore.ls_sid); smsg("%4d LOADS s:%s from %s", current, ! iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; case ISN_LOADG: *************** *** 1865,1871 **** iptr->isn_arg.loadstore.ls_sid); smsg("%4d STORES %s in %s", current, ! iptr->isn_arg.string, si->sn_name); } break; case ISN_STORESCRIPT: --- 1865,1871 ---- iptr->isn_arg.loadstore.ls_sid); smsg("%4d STORES %s in %s", current, ! iptr->isn_arg.loadstore.ls_name, si->sn_name); } break; case ISN_STORESCRIPT: *** ../vim-8.2.0511/src/structs.h 2020-04-03 21:59:29.329665634 +0200 --- src/structs.h 2020-04-05 16:13:46.967427310 +0200 *************** *** 1341,1350 **** typedef struct type_S type_T; struct type_S { vartype_T tt_type; ! short tt_argcount; // for func, partial, -1 for unknown ! short tt_flags; // TTFLAG_ values type_T *tt_member; // for list, dict, func return type ! type_T **tt_args; // func arguments, allocated }; #define TTFLAG_VARARGS 1 // func args ends with "..." --- 1341,1351 ---- typedef struct type_S type_T; struct type_S { vartype_T tt_type; ! char tt_argcount; // for func, -1 for unknown ! char tt_min_argcount; // number of non-optional arguments ! char tt_flags; // TTFLAG_ values type_T *tt_member; // for list, dict, func return type ! type_T **tt_args; // func argument types, allocated }; #define TTFLAG_VARARGS 1 // func args ends with "..." *************** *** 1520,1526 **** int uf_calls; // nr of active calls int uf_cleared; // func_clear() was already called int uf_dfunc_idx; // >= 0 for :def function only ! garray_T uf_args; // arguments garray_T uf_def_args; // default argument expressions // for :def (for :function uf_ret_type is NULL) --- 1521,1527 ---- int uf_calls; // nr of active calls int uf_cleared; // func_clear() was already called int uf_dfunc_idx; // >= 0 for :def function only ! garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions // for :def (for :function uf_ret_type is NULL) *************** *** 1531,1536 **** --- 1532,1538 ---- // uf_def_args; length: uf_def_args.ga_len + 1 char_u *uf_va_name; // name from "...name" or NULL type_T *uf_va_type; // type from "...name: type" or NULL + type_T *uf_func_type; // type of the function, &t_func_any if unknown garray_T uf_lines; // function lines # ifdef FEAT_PROFILE *** ../vim-8.2.0511/src/globals.h 2020-04-03 21:59:29.329665634 +0200 --- src/globals.h 2020-04-03 22:18:25.446684305 +0200 *************** *** 379,417 **** // Commonly used types. ! EXTERN type_T t_any INIT5(VAR_UNKNOWN, 0, 0, NULL, NULL); ! EXTERN type_T t_void INIT5(VAR_VOID, 0, 0, NULL, NULL); ! EXTERN type_T t_bool INIT5(VAR_BOOL, 0, 0, NULL, NULL); ! EXTERN type_T t_special INIT5(VAR_SPECIAL, 0, 0, NULL, NULL); ! EXTERN type_T t_number INIT5(VAR_NUMBER, 0, 0, NULL, NULL); ! EXTERN type_T t_float INIT5(VAR_FLOAT, 0, 0, NULL, NULL); ! EXTERN type_T t_string INIT5(VAR_STRING, 0, 0, NULL, NULL); ! EXTERN type_T t_blob INIT5(VAR_BLOB, 0, 0, NULL, NULL); ! EXTERN type_T t_job INIT5(VAR_JOB, 0, 0, NULL, NULL); ! EXTERN type_T t_channel INIT5(VAR_CHANNEL, 0, 0, NULL, NULL); ! ! EXTERN type_T t_func_void INIT5(VAR_FUNC, -1, 0, &t_void, NULL); ! EXTERN type_T t_func_any INIT5(VAR_FUNC, -1, 0, &t_any, NULL); ! EXTERN type_T t_func_number INIT5(VAR_FUNC, -1, 0, &t_number, NULL); ! EXTERN type_T t_func_string INIT5(VAR_FUNC, -1, 0, &t_string, NULL); ! EXTERN type_T t_func_0_void INIT5(VAR_FUNC, 0, 0, &t_void, NULL); ! EXTERN type_T t_func_0_any INIT5(VAR_FUNC, 0, 0, &t_any, NULL); ! EXTERN type_T t_func_0_number INIT5(VAR_FUNC, 0, 0, &t_number, NULL); ! EXTERN type_T t_func_0_string INIT5(VAR_FUNC, 0, 0, &t_string, NULL); ! ! EXTERN type_T t_list_any INIT5(VAR_LIST, 0, 0, &t_any, NULL); ! EXTERN type_T t_dict_any INIT5(VAR_DICT, 0, 0, &t_any, NULL); ! EXTERN type_T t_list_empty INIT5(VAR_LIST, 0, 0, &t_void, NULL); ! EXTERN type_T t_dict_empty INIT5(VAR_DICT, 0, 0, &t_void, NULL); ! ! EXTERN type_T t_list_bool INIT5(VAR_LIST, 0, 0, &t_bool, NULL); ! EXTERN type_T t_list_number INIT5(VAR_LIST, 0, 0, &t_number, NULL); ! EXTERN type_T t_list_string INIT5(VAR_LIST, 0, 0, &t_string, NULL); ! EXTERN type_T t_list_dict_any INIT5(VAR_LIST, 0, 0, &t_dict_any, NULL); ! ! EXTERN type_T t_dict_bool INIT5(VAR_DICT, 0, 0, &t_bool, NULL); ! EXTERN type_T t_dict_number INIT5(VAR_DICT, 0, 0, &t_number, NULL); ! EXTERN type_T t_dict_string INIT5(VAR_DICT, 0, 0, &t_string, NULL); #endif --- 379,417 ---- // Commonly used types. ! EXTERN type_T t_any INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL); ! EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL); ! ! EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL); ! EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL); ! EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL); ! EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL); ! EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL); ! EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL); ! ! EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL); ! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_void, NULL); ! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_void, NULL); ! ! EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL); ! EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL); ! EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL); ! ! EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL); ! EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL); ! EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL); #endif *** ../vim-8.2.0511/src/vim.h 2020-02-28 22:19:38.310023356 +0100 --- src/vim.h 2020-04-03 22:21:55.937968185 +0200 *************** *** 1751,1756 **** --- 1751,1757 ---- # define INIT3(a, b, c) # define INIT4(a, b, c, d) # define INIT5(a, b, c, d, e) + # define INIT6(a, b, c, d, e, f) #else # ifndef INIT # define INIT(x) x *************** *** 1758,1763 **** --- 1759,1765 ---- # define INIT3(a, b, c) = {a, b, c} # define INIT4(a, b, c, d) = {a, b, c, d} # define INIT5(a, b, c, d, e) = {a, b, c, d, e} + # define INIT6(a, b, c, d, e, f) = {a, b, c, d, e, f} # define DO_INIT # endif #endif *** ../vim-8.2.0511/src/vim9.h 2020-03-31 23:32:27.042315928 +0200 --- src/vim9.h 2020-04-05 16:51:13.266502318 +0200 *************** *** 29,36 **** ISN_STORE, // pop into local variable isn_arg.number ISN_STOREV, // pop into v: variable isn_arg.number ISN_STOREG, // pop into global variable isn_arg.string ! ISN_STORES, // pop into scirpt variable isn_arg.loadstore ! ISN_STORESCRIPT, // pop into scirpt variable isn_arg.script ISN_STOREOPT, // pop into option isn_arg.string ISN_STOREENV, // pop into environment variable isn_arg.string ISN_STOREREG, // pop into register isn_arg.number --- 29,36 ---- ISN_STORE, // pop into local variable isn_arg.number ISN_STOREV, // pop into v: variable isn_arg.number ISN_STOREG, // pop into global variable isn_arg.string ! ISN_STORES, // pop into script variable isn_arg.loadstore ! ISN_STORESCRIPT, // pop into script variable isn_arg.script ISN_STOREOPT, // pop into option isn_arg.string ISN_STOREENV, // pop into environment variable isn_arg.string ISN_STOREREG, // pop into register isn_arg.number *************** *** 191,197 **** // arguments to ISN_LOADS and ISN_STORES typedef struct { ! char_u *ls_name; // variable name int ls_sid; // script ID } loadstore_T; --- 191,197 ---- // arguments to ISN_LOADS and ISN_STORES typedef struct { ! char_u *ls_name; // variable name (with s: for ISN_STORES) int ls_sid; // script ID } loadstore_T; *** ../vim-8.2.0511/src/userfunc.c 2020-04-03 18:13:54.446315826 +0200 --- src/userfunc.c 2020-04-04 22:05:20.769521548 +0200 *************** *** 951,956 **** --- 951,959 ---- VIM_CLEAR(fp->uf_arg_types); VIM_CLEAR(fp->uf_def_arg_idx); VIM_CLEAR(fp->uf_va_name); + while (fp->uf_type_list.ga_len > 0) + vim_free(((type_T **)fp->uf_type_list.ga_data) + [--fp->uf_type_list.ga_len]); ga_clear(&fp->uf_type_list); #ifdef FEAT_PROFILE VIM_CLEAR(fp->uf_tml_count); *************** *** 3013,3018 **** --- 3016,3022 ---- fp->uf_args = newargs; fp->uf_def_args = default_args; fp->uf_ret_type = &t_any; + fp->uf_func_type = &t_func_any; if (eap->cmdidx == CMD_def) { *************** *** 3022,3028 **** SOURCING_LNUM = sourcing_lnum_top; // parse the argument types ! ga_init2(&fp->uf_type_list, sizeof(type_T), 5); if (argtypes.ga_len > 0) { --- 3026,3032 ---- SOURCING_LNUM = sourcing_lnum_top; // parse the argument types ! ga_init2(&fp->uf_type_list, sizeof(type_T *), 10); if (argtypes.ga_len > 0) { *** ../vim-8.2.0511/src/testdir/Make_all.mak 2020-03-22 14:08:27.317399676 +0100 --- src/testdir/Make_all.mak 2020-04-03 22:32:56.115554815 +0200 *************** *** 274,279 **** --- 274,280 ---- test_vartabs \ test_vim9_disassemble \ test_vim9_expr \ + test_vim9_func \ test_vim9_script \ test_viminfo \ test_vimscript \ *************** *** 483,488 **** --- 484,490 ---- test_vartabs.res \ test_vim9_disassemble.res \ test_vim9_expr.res \ + test_vim9_func.res \ test_vim9_script.res \ test_viminfo.res \ test_vimscript.res \ *** ../vim-8.2.0511/src/testdir/test_vim9_script.vim 2020-04-03 21:59:29.333665628 +0200 --- src/testdir/test_vim9_script.vim 2020-04-05 14:40:11.205454801 +0200 *************** *** 21,33 **** let other: list = ['asdf'] enddef - func Test_def_basic() - def SomeFunc(): string - return 'yes' - enddef - call assert_equal('yes', SomeFunc()) - endfunc - let s:appendToMe = 'xxx' let s:addToMe = 111 let g:existing = 'yes' --- 21,26 ---- *************** *** 66,74 **** if has('float') let float1: float = 3.4 endif ! let funky1: func ! let funky2: func = function('len') ! let party2: func = funcref('Test_syntax') " type becomes list let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c'] --- 59,67 ---- if has('float') let float1: float = 3.4 endif ! let Funky1: func ! let Funky2: func = function('len') ! let Party2: func = funcref('Test_syntax') " type becomes list let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c'] *************** *** 134,139 **** --- 127,135 ---- assert_equal('noneagain', v:errmsg) call CheckDefFailure(['v:errmsg += "more"'], 'E1013:') call CheckDefFailure(['v:errmsg += 123'], 'E1013:') + enddef + + def Test_assignment_default() " Test default values. let thebool: bool *************** *** 153,160 **** let theblob: blob assert_equal(0z, theblob) ! let thefunc: func ! assert_equal(test_null_function(), thefunc) let thelist: list assert_equal([], thelist) --- 149,156 ---- let theblob: blob assert_equal(0z, theblob) ! let Thefunc: func ! assert_equal(test_null_function(), Thefunc) let thelist: list assert_equal([], thelist) *************** *** 264,420 **** enddef - def ReturnString(): string - return 'string' - enddef - - def ReturnNumber(): number - return 123 - enddef - - let g:notNumber = 'string' - - def ReturnGlobal(): number - return g:notNumber - enddef - - def Test_return_something() - assert_equal('string', ReturnString()) - assert_equal(123, ReturnNumber()) - assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string') - enddef - - let s:nothing = 0 - def ReturnNothing() - s:nothing = 1 - if true - return - endif - s:nothing = 2 - enddef - - def Test_return_nothing() - ReturnNothing() - assert_equal(1, s:nothing) - enddef - - func Increment() - let g:counter += 1 - endfunc - - def Test_call_ufunc_count() - g:counter = 1 - Increment() - Increment() - Increment() - " works with and without :call - assert_equal(4, g:counter) - call assert_equal(4, g:counter) - unlet g:counter - enddef - - def MyVarargs(arg: string, ...rest: list): string - let res = arg - for s in rest - res ..= ',' .. s - endfor - return res - enddef - - def Test_call_varargs() - assert_equal('one', MyVarargs('one')) - assert_equal('one,two', MyVarargs('one', 'two')) - assert_equal('one,two,three', MyVarargs('one', 'two', 'three')) - enddef - - def MyDefaultArgs(name = 'string'): string - return name - enddef - - def Test_call_default_args() - assert_equal('string', MyDefaultArgs()) - assert_equal('one', MyDefaultArgs('one')) - assert_fails('call MyDefaultArgs("one", "two")', 'E118:') - - call CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef'], 'E1001:') - enddef - - func Test_call_default_args_from_func() - call assert_equal('string', MyDefaultArgs()) - call assert_equal('one', MyDefaultArgs('one')) - call assert_fails('call MyDefaultArgs("one", "two")', 'E118:') - endfunc - - func TakesOneArg(arg) - echo a:arg - endfunc - - def Test_call_wrong_args() - call CheckDefFailure(['TakesOneArg()'], 'E119:') - call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:') - call CheckDefFailure(['bufnr(xxx)'], 'E1001:') - enddef - - " Default arg and varargs - def MyDefVarargs(one: string, two = 'foo', ...rest: list): string - let res = one .. ',' .. two - for s in rest - res ..= ',' .. s - endfor - return res - enddef - - def Test_call_def_varargs() - call assert_fails('call MyDefVarargs()', 'E119:') - assert_equal('one,foo', MyDefVarargs('one')) - assert_equal('one,two', MyDefVarargs('one', 'two')) - assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three')) - enddef - - def Test_using_var_as_arg() - call writefile(['def Func(x: number)', 'let x = 234', 'enddef'], 'Xdef') - call assert_fails('so Xdef', 'E1006:') - call delete('Xdef') - enddef - - def Test_call_func_defined_later() - call assert_equal('one', DefinedLater('one')) - call assert_fails('call NotDefined("one")', 'E117:') - enddef - - func DefinedLater(arg) - return a:arg - endfunc - - def FuncWithForwardCall() - return DefinedEvenLater("yes") - enddef - - def DefinedEvenLater(arg: string): string - return arg - enddef - - def Test_error_in_nested_function() - " Error in called function requires unwinding the call stack. - assert_fails('call FuncWithForwardCall()', 'E1029') - enddef - - def Test_return_type_wrong() - CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string') - CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number') - CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string') - CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string') - - CheckScriptFailure(['def Func(): number', 'return', 'enddef'], 'E1003:') - - CheckScriptFailure(['def Func(): list', 'return []', 'enddef'], 'E1008:') - CheckScriptFailure(['def Func(): dict', 'return {}', 'enddef'], 'E1008:') - enddef - - def Test_arg_type_wrong() - CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing ') - enddef - def Test_try_catch() let l = [] try --- 260,265 ---- *************** *** 732,813 **** assert_fails('export something', 'E1042') enddef - def Test_vim9script_call() - let lines =<< trim END - vim9script - let var = '' - def MyFunc(arg: string) - var = arg - enddef - MyFunc('foobar') - assert_equal('foobar', var) - - let str = 'barfoo' - str->MyFunc() - assert_equal('barfoo', var) - - let g:value = 'value' - g:value->MyFunc() - assert_equal('value', var) - - let listvar = [] - def ListFunc(arg: list) - listvar = arg - enddef - [1, 2, 3]->ListFunc() - assert_equal([1, 2, 3], listvar) - - let dictvar = {} - def DictFunc(arg: dict) - dictvar = arg - enddef - {'a': 1, 'b': 2}->DictFunc() - assert_equal(#{a: 1, b: 2}, dictvar) - def CompiledDict() - {'a': 3, 'b': 4}->DictFunc() - enddef - CompiledDict() - assert_equal(#{a: 3, b: 4}, dictvar) - - #{a: 3, b: 4}->DictFunc() - assert_equal(#{a: 3, b: 4}, dictvar) - - ('text')->MyFunc() - assert_equal('text', var) - ("some")->MyFunc() - assert_equal('some', var) - END - writefile(lines, 'Xcall.vim') - source Xcall.vim - delete('Xcall.vim') - enddef - - def Test_vim9script_call_fail_decl() - let lines =<< trim END - vim9script - let var = '' - def MyFunc(arg: string) - let var = 123 - enddef - END - writefile(lines, 'Xcall_decl.vim') - assert_fails('source Xcall_decl.vim', 'E1054:') - delete('Xcall_decl.vim') - enddef - - def Test_vim9script_call_fail_const() - let lines =<< trim END - vim9script - const var = '' - def MyFunc(arg: string) - var = 'asdf' - enddef - END - writefile(lines, 'Xcall_const.vim') - assert_fails('source Xcall_const.vim', 'E46:') - delete('Xcall_const.vim') - enddef - def Test_vim9script_reload() let lines =<< trim END vim9script --- 577,582 ---- *************** *** 926,942 **** assert_equal([2, 99, 3, 4, 5], l) enddef - " Test that inside :function a Python function can be defined, :def is not - " recognized. - func Test_function_python() - CheckFeature python3 - let py = 'python3' - execute py "<< EOF" - def do_something(): - return 1 - EOF - endfunc - def IfElse(what: number): string let res = '' if what == 1 --- 695,700 ---- *************** *** 1087,1113 **** call CheckDefFailure(["if has('aaa') ? true false"], 'E109:') enddef - def Test_delfunc() - let lines =<< trim END - vim9script - def GoneSoon() - echo 'hello' - enddef - - def CallGoneSoon() - GoneSoon() - enddef - - delfunc GoneSoon - CallGoneSoon() - END - writefile(lines, 'XToDelFunc') - assert_fails('so XToDelFunc', 'E933') - assert_fails('so XToDelFunc', 'E933') - - delete('XToDelFunc') - enddef - def Test_execute_cmd() new setline(1, 'default') --- 845,850 ---- *************** *** 1222,1304 **** assert_true(caught, 'should have caught an exception') enddef - def Test_redef_failure() - call writefile(['def Func0(): string', 'return "Func0"', 'enddef'], 'Xdef') - so Xdef - call writefile(['def Func1(): string', 'return "Func1"', 'enddef'], 'Xdef') - so Xdef - call writefile(['def! Func0(): string', 'enddef'], 'Xdef') - call assert_fails('so Xdef', 'E1027:') - call writefile(['def Func2(): string', 'return "Func2"', 'enddef'], 'Xdef') - so Xdef - call delete('Xdef') - - call assert_equal(0, Func0()) - call assert_equal('Func1', Func1()) - call assert_equal('Func2', Func2()) - - delfunc! Func0 - delfunc! Func1 - delfunc! Func2 - enddef - - " Test for internal functions returning different types - func Test_InternalFuncRetType() - let lines =<< trim END - def RetFloat(): float - return ceil(1.456) - enddef - - def RetListAny(): list - return items({'k' : 'v'}) - enddef - - def RetListString(): list - return split('a:b:c', ':') - enddef - - def RetListDictAny(): list> - return getbufinfo() - enddef - - def RetDictNumber(): dict - return wordcount() - enddef - - def RetDictString(): dict - return environ() - enddef - END - call writefile(lines, 'Xscript') - source Xscript - - call assert_equal(2.0, RetFloat()) - call assert_equal([['k', 'v']], RetListAny()) - call assert_equal(['a', 'b', 'c'], RetListString()) - call assert_notequal([], RetListDictAny()) - call assert_notequal({}, RetDictNumber()) - call assert_notequal({}, RetDictString()) - call delete('Xscript') - endfunc - - " Test for passing too many or too few arguments to internal functions - func Test_internalfunc_arg_error() - let l =<< trim END - def! FArgErr(): float - return ceil(1.1, 2) - enddef - END - call writefile(l, 'Xinvalidarg') - call assert_fails('so Xinvalidarg', 'E118:') - let l =<< trim END - def! FArgErr(): float - return ceil() - enddef - END - call writefile(l, 'Xinvalidarg') - call assert_fails('so Xinvalidarg', 'E119:') - call delete('Xinvalidarg') - endfunc " Keep this last, it messes up highlighting. def Test_substitute_cmd() --- 959,964 ---- *** ../vim-8.2.0511/src/testdir/test_vim9_expr.vim 2020-04-03 21:59:29.329665634 +0200 --- src/testdir/test_vim9_expr.vim 2020-04-04 22:29:19.048946562 +0200 *************** *** 460,467 **** call CheckDefFailureMult(['let j: job', 'let chan: channel', 'let r = j == chan'], 'Cannot compare job with channel') call CheckDefFailureMult(['let j: job', 'let x: list', 'let r = j == x'], 'Cannot compare job with list') ! call CheckDefFailureMult(['let j: job', 'let x: func', 'let r = j == x'], 'Cannot compare job with func') ! call CheckDefFailureMult(['let j: job', 'let x: func', 'let r = j == x'], 'Cannot compare job with func') endfunc " test addition, subtraction, concatenation --- 460,467 ---- call CheckDefFailureMult(['let j: job', 'let chan: channel', 'let r = j == chan'], 'Cannot compare job with channel') call CheckDefFailureMult(['let j: job', 'let x: list', 'let r = j == x'], 'Cannot compare job with list') ! call CheckDefFailureMult(['let j: job', 'let Xx: func', 'let r = j == Xx'], 'Cannot compare job with func') ! call CheckDefFailureMult(['let j: job', 'let Xx: func', 'let r = j == Xx'], 'Cannot compare job with func') endfunc " test addition, subtraction, concatenation *** ../vim-8.2.0511/src/testdir/test_vim9_func.vim 2020-04-05 17:04:23.551397938 +0200 --- src/testdir/test_vim9_func.vim 2020-04-05 16:08:12.572796447 +0200 *************** *** 0 **** --- 1,400 ---- + " Test various aspects of the Vim9 script language. + + source check.vim + source view_util.vim + + " Check that "lines" inside ":def" results in an "error" message. + func CheckDefFailure(lines, error) + call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef') + call assert_fails('so Xdef', a:error, a:lines) + call delete('Xdef') + endfunc + + func CheckScriptFailure(lines, error) + call writefile(a:lines, 'Xdef') + call assert_fails('so Xdef', a:error, a:lines) + call delete('Xdef') + endfunc + + func Test_def_basic() + def SomeFunc(): string + return 'yes' + enddef + call assert_equal('yes', SomeFunc()) + endfunc + + def ReturnString(): string + return 'string' + enddef + + def ReturnNumber(): number + return 123 + enddef + + let g:notNumber = 'string' + + def ReturnGlobal(): number + return g:notNumber + enddef + + def Test_return_something() + assert_equal('string', ReturnString()) + assert_equal(123, ReturnNumber()) + assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string') + enddef + + let s:nothing = 0 + def ReturnNothing() + s:nothing = 1 + if true + return + endif + s:nothing = 2 + enddef + + def Test_return_nothing() + ReturnNothing() + assert_equal(1, s:nothing) + enddef + + func Increment() + let g:counter += 1 + endfunc + + def Test_call_ufunc_count() + g:counter = 1 + Increment() + Increment() + Increment() + " works with and without :call + assert_equal(4, g:counter) + call assert_equal(4, g:counter) + unlet g:counter + enddef + + def MyVarargs(arg: string, ...rest: list): string + let res = arg + for s in rest + res ..= ',' .. s + endfor + return res + enddef + + def Test_call_varargs() + assert_equal('one', MyVarargs('one')) + assert_equal('one,two', MyVarargs('one', 'two')) + assert_equal('one,two,three', MyVarargs('one', 'two', 'three')) + enddef + + def MyDefaultArgs(name = 'string'): string + return name + enddef + + def Test_call_default_args() + assert_equal('string', MyDefaultArgs()) + assert_equal('one', MyDefaultArgs('one')) + assert_fails('call MyDefaultArgs("one", "two")', 'E118:') + + call CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef'], 'E1001:') + enddef + + func Test_call_default_args_from_func() + call assert_equal('string', MyDefaultArgs()) + call assert_equal('one', MyDefaultArgs('one')) + call assert_fails('call MyDefaultArgs("one", "two")', 'E118:') + endfunc + + func TakesOneArg(arg) + echo a:arg + endfunc + + def Test_call_wrong_args() + call CheckDefFailure(['TakesOneArg()'], 'E119:') + call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:') + call CheckDefFailure(['bufnr(xxx)'], 'E1001:') + enddef + + " Default arg and varargs + def MyDefVarargs(one: string, two = 'foo', ...rest: list): string + let res = one .. ',' .. two + for s in rest + res ..= ',' .. s + endfor + return res + enddef + + def Test_call_def_varargs() + call assert_fails('call MyDefVarargs()', 'E119:') + assert_equal('one,foo', MyDefVarargs('one')) + assert_equal('one,two', MyDefVarargs('one', 'two')) + assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three')) + enddef + + def Test_using_var_as_arg() + call writefile(['def Func(x: number)', 'let x = 234', 'enddef'], 'Xdef') + call assert_fails('so Xdef', 'E1006:') + call delete('Xdef') + enddef + + def Test_call_func_defined_later() + call assert_equal('one', DefinedLater('one')) + call assert_fails('call NotDefined("one")', 'E117:') + enddef + + func DefinedLater(arg) + return a:arg + endfunc + + def FuncWithForwardCall() + return DefinedEvenLater("yes") + enddef + + def DefinedEvenLater(arg: string): string + return arg + enddef + + def Test_error_in_nested_function() + " Error in called function requires unwinding the call stack. + assert_fails('call FuncWithForwardCall()', 'E1029') + enddef + + def Test_return_type_wrong() + CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string') + CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number') + CheckScriptFailure(['def Func(): void', 'return "a"', 'enddef'], 'expected void but got string') + CheckScriptFailure(['def Func()', 'return "a"', 'enddef'], 'expected void but got string') + + CheckScriptFailure(['def Func(): number', 'return', 'enddef'], 'E1003:') + + CheckScriptFailure(['def Func(): list', 'return []', 'enddef'], 'E1008:') + CheckScriptFailure(['def Func(): dict', 'return {}', 'enddef'], 'E1008:') + enddef + + def Test_arg_type_wrong() + CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing ') + enddef + + def Test_vim9script_call() + let lines =<< trim END + vim9script + let var = '' + def MyFunc(arg: string) + var = arg + enddef + MyFunc('foobar') + assert_equal('foobar', var) + + let str = 'barfoo' + str->MyFunc() + assert_equal('barfoo', var) + + let g:value = 'value' + g:value->MyFunc() + assert_equal('value', var) + + let listvar = [] + def ListFunc(arg: list) + listvar = arg + enddef + [1, 2, 3]->ListFunc() + assert_equal([1, 2, 3], listvar) + + let dictvar = {} + def DictFunc(arg: dict) + dictvar = arg + enddef + {'a': 1, 'b': 2}->DictFunc() + assert_equal(#{a: 1, b: 2}, dictvar) + def CompiledDict() + {'a': 3, 'b': 4}->DictFunc() + enddef + CompiledDict() + assert_equal(#{a: 3, b: 4}, dictvar) + + #{a: 3, b: 4}->DictFunc() + assert_equal(#{a: 3, b: 4}, dictvar) + + ('text')->MyFunc() + assert_equal('text', var) + ("some")->MyFunc() + assert_equal('some', var) + END + writefile(lines, 'Xcall.vim') + source Xcall.vim + delete('Xcall.vim') + enddef + + def Test_vim9script_call_fail_decl() + let lines =<< trim END + vim9script + let var = '' + def MyFunc(arg: string) + let var = 123 + enddef + END + writefile(lines, 'Xcall_decl.vim') + assert_fails('source Xcall_decl.vim', 'E1054:') + delete('Xcall_decl.vim') + enddef + + def Test_vim9script_call_fail_const() + let lines =<< trim END + vim9script + const var = '' + def MyFunc(arg: string) + var = 'asdf' + enddef + END + writefile(lines, 'Xcall_const.vim') + assert_fails('source Xcall_const.vim', 'E46:') + delete('Xcall_const.vim') + enddef + + " Test that inside :function a Python function can be defined, :def is not + " recognized. + func Test_function_python() + CheckFeature python3 + let py = 'python3' + execute py "<< EOF" + def do_something(): + return 1 + EOF + endfunc + + def Test_delfunc() + let lines =<< trim END + vim9script + def GoneSoon() + echo 'hello' + enddef + + def CallGoneSoon() + GoneSoon() + enddef + + delfunc GoneSoon + CallGoneSoon() + END + writefile(lines, 'XToDelFunc') + assert_fails('so XToDelFunc', 'E933') + assert_fails('so XToDelFunc', 'E933') + + delete('XToDelFunc') + enddef + + def Test_redef_failure() + call writefile(['def Func0(): string', 'return "Func0"', 'enddef'], 'Xdef') + so Xdef + call writefile(['def Func1(): string', 'return "Func1"', 'enddef'], 'Xdef') + so Xdef + call writefile(['def! Func0(): string', 'enddef'], 'Xdef') + call assert_fails('so Xdef', 'E1027:') + call writefile(['def Func2(): string', 'return "Func2"', 'enddef'], 'Xdef') + so Xdef + call delete('Xdef') + + call assert_equal(0, Func0()) + call assert_equal('Func1', Func1()) + call assert_equal('Func2', Func2()) + + delfunc! Func0 + delfunc! Func1 + delfunc! Func2 + enddef + + " Test for internal functions returning different types + func Test_InternalFuncRetType() + let lines =<< trim END + def RetFloat(): float + return ceil(1.456) + enddef + + def RetListAny(): list + return items({'k' : 'v'}) + enddef + + def RetListString(): list + return split('a:b:c', ':') + enddef + + def RetListDictAny(): list> + return getbufinfo() + enddef + + def RetDictNumber(): dict + return wordcount() + enddef + + def RetDictString(): dict + return environ() + enddef + END + call writefile(lines, 'Xscript') + source Xscript + + call assert_equal(2.0, RetFloat()) + call assert_equal([['k', 'v']], RetListAny()) + call assert_equal(['a', 'b', 'c'], RetListString()) + call assert_notequal([], RetListDictAny()) + call assert_notequal({}, RetDictNumber()) + call assert_notequal({}, RetDictString()) + call delete('Xscript') + endfunc + + " Test for passing too many or too few arguments to internal functions + func Test_internalfunc_arg_error() + let l =<< trim END + def! FArgErr(): float + return ceil(1.1, 2) + enddef + END + call writefile(l, 'Xinvalidarg') + call assert_fails('so Xinvalidarg', 'E118:') + let l =<< trim END + def! FArgErr(): float + return ceil() + enddef + END + call writefile(l, 'Xinvalidarg') + call assert_fails('so Xinvalidarg', 'E119:') + call delete('Xinvalidarg') + endfunc + + let s:funcResult = 0 + + def FuncNoArgNoRet() + funcResult = 11 + enddef + + def FuncNoArgRetNumber(): number + funcResult = 22 + return 1234 + enddef + + def FuncOneArgNoRet(arg: number) + funcResult = arg + enddef + + def FuncOneArgRetNumber(arg: number): number + funcResult = arg + return arg + enddef + + def Test_func_type() + let Ref1: func() + funcResult = 0 + Ref1 = FuncNoArgNoRet + Ref1() + assert_equal(11, funcResult) + enddef + + def Test_func_type_fails() + CheckDefFailure(['let ref1: func()'], 'E704:') + + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1013: type mismatch, expected func() but got func(): number') + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1013: type mismatch, expected func() but got func(number)') + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1013: type mismatch, expected func() but got func(number): number') + enddef + + + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-8.2.0511/src/testdir/test_vim9_disassemble.vim 2020-04-03 21:59:29.333665628 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-04-05 16:38:21.993652926 +0200 *************** *** 87,94 **** --- 87,96 ---- def Test_disassemble_store() let res = execute('disass s:ScriptFuncStore') assert_match('\d*_ScriptFuncStore.*' + \ .. 'let localnr = 1.*' \ .. 'localnr = 2.*' \ .. ' STORE 2 in $0.*' + \ .. 'let localstr = ''abc''.*' \ .. 'localstr = ''xyz''.*' \ .. ' STORE $1.*' \ .. 'v:char = ''abc''.*' *************** *** 360,381 **** enddef def WithFunc() ! let funky1: func ! let funky2: func = function("len") ! let party2: func = funcref("UserFunc") enddef def Test_disassemble_function() let instr = execute('disassemble WithFunc') assert_match('WithFunc.*' ! \ .. 'let funky1: func.*' \ .. '0 PUSHFUNC "\[none]".*' \ .. '1 STORE $0.*' ! \ .. 'let funky2: func = function("len").*' \ .. '2 PUSHS "len".*' \ .. '3 BCALL function(argc 1).*' \ .. '4 STORE $1.*' ! \ .. 'let party2: func = funcref("UserFunc").*' \ .. '\d PUSHS "UserFunc".*' \ .. '\d BCALL funcref(argc 1).*' \ .. '\d STORE $2.*' --- 362,383 ---- enddef def WithFunc() ! let Funky1: func ! let Funky2: func = function("len") ! let Party2: func = funcref("UserFunc") enddef def Test_disassemble_function() let instr = execute('disassemble WithFunc') assert_match('WithFunc.*' ! \ .. 'let Funky1: func.*' \ .. '0 PUSHFUNC "\[none]".*' \ .. '1 STORE $0.*' ! \ .. 'let Funky2: func = function("len").*' \ .. '2 PUSHS "len".*' \ .. '3 BCALL function(argc 1).*' \ .. '4 STORE $1.*' ! \ .. 'let Party2: func = funcref("UserFunc").*' \ .. '\d PUSHS "UserFunc".*' \ .. '\d BCALL funcref(argc 1).*' \ .. '\d STORE $2.*' *** ../vim-8.2.0511/src/version.c 2020-04-05 15:36:13.023853428 +0200 --- src/version.c 2020-04-05 17:01:41.692030123 +0200 *************** *** 740,741 **** --- 740,743 ---- { /* Add new patch number below this line */ + /**/ + 512, /**/ -- Every exit is an entrance into something else. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///