# HG changeset patch # User Bram Moolenaar # Date 1608896705 -3600 # Node ID cdb706d5c43df4d36862d4603ab4fe36764e108a # Parent c5b1246b95573b298d868091d027dcb969484179 patch 8.2.2209: Vim9: return type of => lambda not parsed Commit: https://github.com/vim/vim/commit/9e68c32563d8c9ffe1ac04ecd4ccd730af66b97c Author: Bram Moolenaar Date: Fri Dec 25 12:38:04 2020 +0100 patch 8.2.2209: Vim9: return type of => lambda not parsed Problem: Vim9: return type of => lambda not parsed. Solution: Parse and use the return type. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -868,7 +868,9 @@ get_lval( char_u *tp = skipwhite(p + 1); // parse the type after the name - lp->ll_type = parse_type(&tp, &si->sn_type_list); + lp->ll_type = parse_type(&tp, &si->sn_type_list, !quiet); + if (lp->ll_type == NULL && !quiet) + return NULL; lp->ll_name_end = tp; } } diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -17,7 +17,7 @@ void arg_type_mismatch(type_T *expected, int check_type(type_T *expected, type_T *actual, int give_msg, int argidx); int check_arg_type(type_T *expected, type_T *actual, int argidx); char_u *skip_type(char_u *start, int optional); -type_T *parse_type(char_u **arg, garray_T *type_gap); +type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error); void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap); type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap); char *vartype_name(vartype_T type); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1951,7 +1951,7 @@ def Test_expr7_new_lambda() assert_equal([1, 3, 5], res) # Lambda returning a dict - var Lmb = () => {key: 42} + var Lmb = () => ({key: 42}) assert_equal({key: 42}, Lmb()) END CheckDefSuccess(lines) @@ -1960,11 +1960,16 @@ def Test_expr7_new_lambda() CheckDefFailure(["var Ref = (a)=> a + 1"], 'E1001:') CheckDefFailure(["var Ref = (a) =>a + 1"], 'E1001:') + CheckDefSuccess(["var Ref: func(number): string = (a: number): string => 'x'"]) + CheckDefSuccess(["var Ref: func(number): any = (a: number): any => 'x'"]) + CheckDefFailure(["var Ref: func(number): number = (a: number): string => 'x'"], 'E1012:') + CheckDefFailure(["var Ref: func(number): string = (a: number): string => 99"], 'E1012:') + CheckDefFailure(["filter([1, 2], (k,v) => 1)"], 'E1069:', 1) # error is in first line of the lambda CheckDefFailure(["var L = (a) -> a + b"], 'E1001:', 1) -# TODO: lambda after -> doesn't work yet +# TODO: ->(lambda)() doesn't work yet # assert_equal('xxxyyy', 'xxx'->((a, b) => a .. b)('yyy')) # CheckDefExecFailure(["var s = 'asdf'->{a -> a}('x')"], @@ -1973,9 +1978,9 @@ def Test_expr7_new_lambda() # 'E1106: 2 arguments too many') # CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1) - CheckDefSuccess(['var Fx = (a) => {k1: 0,', ' k2: 1}']) - CheckDefFailure(['var Fx = (a) => {k1: 0', ' k2: 1}'], 'E722:', 2) - CheckDefFailure(['var Fx = (a) => {k1: 0,', ' k2 1}'], 'E720:', 2) + CheckDefSuccess(['var Fx = (a) => ({k1: 0,', ' k2: 1})']) + CheckDefFailure(['var Fx = (a) => ({k1: 0', ' k2: 1})'], 'E722:', 2) + CheckDefFailure(['var Fx = (a) => ({k1: 0,', ' k2 1})'], 'E720:', 2) CheckDefSuccess(['var Fx = (a) => [0,', ' 1]']) CheckDefFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -349,7 +349,7 @@ parse_argument_types(ufunc_T *fp, garray // will get the type from the default value type = &t_unknown; else - type = parse_type(&p, &fp->uf_type_list); + type = parse_type(&p, &fp->uf_type_list, TRUE); if (type == NULL) return FAIL; fp->uf_arg_types[i] = type; @@ -369,7 +369,7 @@ parse_argument_types(ufunc_T *fp, garray // todo: get type from default value fp->uf_va_type = &t_any; else - fp->uf_va_type = parse_type(&p, &fp->uf_type_list); + fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); if (fp->uf_va_type == NULL) return FAIL; } @@ -460,17 +460,22 @@ register_cfunc(cfunc_T cb, cfunc_free_T /* * Skip over "->" or "=>" after the arguments of a lambda. + * If ": type" is found make "ret_type" point to "type". * Return NULL if no valid arrow found. */ static char_u * -skip_arrow(char_u *start, int equal_arrow) +skip_arrow(char_u *start, int equal_arrow, char_u **ret_type) { char_u *s = start; if (equal_arrow) { if (*s == ':') - s = skip_type(skipwhite(s + 1), TRUE); + { + s = skipwhite(s + 1); + *ret_type = s; + s = skip_type(s, TRUE); + } s = skipwhite(s); if (*s != '=') return NULL; @@ -503,6 +508,7 @@ get_lambda_tv( ufunc_T *fp = NULL; partial_T *pt = NULL; int varargs; + char_u *ret_type = NULL; int ret; char_u *s; char_u *start, *end; @@ -517,19 +523,20 @@ get_lambda_tv( ga_init(&newargs); ga_init(&newlines); - // First, check if this is a lambda expression. "->" or "=>" must exist. + // First, check if this is really a lambda expression. "->" or "=>" must + // be found after the arguments. s = skipwhite(*arg + 1); ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, types_optional ? &argtypes : NULL, types_optional, NULL, NULL, TRUE, NULL, NULL); - if (ret == FAIL || skip_arrow(s, equal_arrow) == NULL) + if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type) == NULL) { if (types_optional) ga_clear_strings(&argtypes); return NOTDONE; } - // Parse the arguments again. + // Parse the arguments for real. if (evaluate) pnewargs = &newargs; else @@ -538,7 +545,8 @@ get_lambda_tv( ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, &varargs, NULL, FALSE, NULL, NULL); - if (ret == FAIL || (*arg = skip_arrow(*arg, equal_arrow)) == NULL) + if (ret == FAIL + || (*arg = skip_arrow(*arg, equal_arrow, &ret_type)) == NULL) { if (types_optional) ga_clear_strings(&argtypes); @@ -551,11 +559,11 @@ get_lambda_tv( *arg = skipwhite_and_linebreak(*arg, evalarg); - // Only recognize "{" as the start of a function body when followed by - // white space, "{key: val}" is a dict. - if (equal_arrow && **arg == '{' && IS_WHITE_OR_NUL((*arg)[1])) + // Recognize "{" as the start of a function body. + if (equal_arrow && **arg == '{') { // TODO: process the function body upto the "}". + // Return type is required then. emsg("Lambda function body not supported yet"); goto errret; } @@ -619,9 +627,18 @@ get_lambda_tv( hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; ga_init(&fp->uf_def_args); - if (types_optional - && parse_argument_types(fp, &argtypes, FALSE) == FAIL) - goto errret; + if (types_optional) + { + if (parse_argument_types(fp, &argtypes, FALSE) == FAIL) + goto errret; + if (ret_type != NULL) + { + fp->uf_ret_type = parse_type(&ret_type, + &fp->uf_type_list, TRUE); + if (fp->uf_ret_type == NULL) + goto errret; + } + } fp->uf_lines = newlines; if (current_funccal != NULL && eval_lavars) @@ -3752,7 +3769,7 @@ define_function(exarg_T *eap, char_u *na else { p = ret_type; - fp->uf_ret_type = parse_type(&p, &fp->uf_type_list); + fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE); } SOURCING_LNUM = lnum_save; } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2209, +/**/ 2208, /**/ 2207, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4118,11 +4118,9 @@ compile_expr7t(char_u **arg, cctx_T *cct // Recognize if (**arg == '<' && eval_isnamec1((*arg)[1])) { - int called_emsg_before = called_emsg; - ++*arg; - want_type = parse_type(arg, cctx->ctx_type_list); - if (called_emsg != called_emsg_before) + want_type = parse_type(arg, cctx->ctx_type_list, TRUE); + if (want_type == NULL) return FAIL; if (**arg != '>') @@ -4809,7 +4807,7 @@ compile_expr0(char_u **arg, cctx_T *cct * compile "return [expr]" */ static char_u * -compile_return(char_u *arg, int set_return_type, cctx_T *cctx) +compile_return(char_u *arg, int check_return_type, cctx_T *cctx) { char_u *p = arg; garray_T *stack = &cctx->ctx_type_stack; @@ -4824,8 +4822,10 @@ compile_return(char_u *arg, int set_retu if (cctx->ctx_skip != SKIP_YES) { stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (set_return_type) + if (check_return_type && cctx->ctx_ufunc->uf_ret_type == NULL) + { cctx->ctx_ufunc->uf_ret_type = stack_type; + } else { if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID @@ -4843,7 +4843,7 @@ compile_return(char_u *arg, int set_retu } else { - // "set_return_type" cannot be TRUE, only used for a lambda which + // "check_return_type" cannot be TRUE, only used for a lambda which // always has an argument. if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID && cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_UNKNOWN) @@ -5636,7 +5636,9 @@ compile_assignment(char_u *arg, exarg_T goto theend; } p = skipwhite(var_end + 1); - type = parse_type(&p, cctx->ctx_type_list); + type = parse_type(&p, cctx->ctx_type_list, TRUE); + if (type == NULL) + goto theend; has_type = TRUE; } else if (lvar != NULL) @@ -7417,15 +7419,16 @@ add_def_function(ufunc_T *ufunc) * After ex_function() has collected all the function lines: parse and compile * the lines into instructions. * Adds the function to "def_functions". - * When "set_return_type" is set then set ufunc->uf_ret_type to the type of the - * return statement (used for lambda). + * When "check_return_type" is set then set ufunc->uf_ret_type to the type of + * the return statement (used for lambda). When uf_ret_type is already set + * then check that it matches. * "outer_cctx" is set for a nested function. * This can be used recursively through compile_lambda(), which may reallocate * "def_functions". * Returns OK or FAIL. */ int -compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) +compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx) { char_u *line = NULL; char_u *p; @@ -7797,7 +7800,7 @@ compile_def_function(ufunc_T *ufunc, int goto erret; case CMD_return: - line = compile_return(p, set_return_type, &cctx); + line = compile_return(p, check_return_type, &cctx); cctx.ctx_had_return = TRUE; break; diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -511,7 +511,6 @@ vim9_declare_scriptvar(exarg_T *eap, cha char_u *name; scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); type_T *type; - int called_emsg_before = called_emsg; typval_T init_tv; if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) @@ -548,8 +547,8 @@ vim9_declare_scriptvar(exarg_T *eap, cha // parse type p = skipwhite(p + 1); - type = parse_type(&p, &si->sn_type_list); - if (called_emsg != called_emsg_before) + type = parse_type(&p, &si->sn_type_list, TRUE); + if (type == NULL) { vim_free(name); return p; diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -596,28 +596,38 @@ skip_type(char_u *start, int optional) * Returns NULL in case of failure. */ static type_T * -parse_type_member(char_u **arg, type_T *type, garray_T *type_gap) +parse_type_member( + char_u **arg, + type_T *type, + garray_T *type_gap, + int give_error) { type_T *member_type; int prev_called_emsg = called_emsg; if (**arg != '<') { - if (*skipwhite(*arg) == '<') - semsg(_(e_no_white_space_allowed_before_str), "<"); - else - emsg(_(e_missing_type)); - return type; + if (give_error) + { + if (*skipwhite(*arg) == '<') + semsg(_(e_no_white_space_allowed_before_str), "<"); + else + emsg(_(e_missing_type)); + } + return NULL; } *arg = skipwhite(*arg + 1); - member_type = parse_type(arg, type_gap); + member_type = parse_type(arg, type_gap, give_error); + if (member_type == NULL) + return NULL; *arg = skipwhite(*arg); if (**arg != '>' && called_emsg == prev_called_emsg) { - emsg(_(e_missing_gt_after_type)); - return type; + if (give_error) + emsg(_(e_missing_gt_after_type)); + return NULL; } ++*arg; @@ -628,10 +638,11 @@ parse_type_member(char_u **arg, type_T * /* * Parse a type at "arg" and advance over it. - * Return &t_any for failure. + * When "give_error" is TRUE give error messages, otherwise be quiet. + * Return NULL for failure. */ type_T * -parse_type(char_u **arg, garray_T *type_gap) +parse_type(char_u **arg, garray_T *type_gap, int give_error) { char_u *p = *arg; size_t len; @@ -673,7 +684,8 @@ parse_type(char_u **arg, garray_T *type_ if (len == 4 && STRNCMP(*arg, "dict", len) == 0) { *arg += len; - return parse_type_member(arg, &t_dict_any, type_gap); + return parse_type_member(arg, &t_dict_any, + type_gap, give_error); } break; case 'f': @@ -683,8 +695,9 @@ parse_type(char_u **arg, garray_T *type_ *arg += len; return &t_float; #else - emsg(_(e_this_vim_is_not_compiled_with_float_support)); - return &t_any; + if (give_error) + emsg(_(e_this_vim_is_not_compiled_with_float_support)); + return NULL; #endif } if (len == 4 && STRNCMP(*arg, "func", len) == 0) @@ -721,11 +734,15 @@ parse_type(char_u **arg, garray_T *type_ } else if (first_optional != -1) { - emsg(_(e_mandatory_argument_after_optional_argument)); - return &t_any; + if (give_error) + emsg(_(e_mandatory_argument_after_optional_argument)); + return NULL; } - arg_type[argcount++] = parse_type(&p, type_gap); + type = parse_type(&p, type_gap, give_error); + if (type == NULL) + return NULL; + arg_type[argcount++] = type; // Nothing comes after "...{type}". if (flags & TTFLAG_VARARGS) @@ -733,31 +750,35 @@ parse_type(char_u **arg, garray_T *type_ if (*p != ',' && *skipwhite(p) == ',') { - semsg(_(e_no_white_space_allowed_before_str), ","); - return &t_any; + if (give_error) + semsg(_(e_no_white_space_allowed_before_str), ","); + return NULL; } if (*p == ',') { ++p; if (!VIM_ISWHITE(*p)) { - semsg(_(e_white_space_required_after_str), ","); - return &t_any; + if (give_error) + semsg(_(e_white_space_required_after_str), ","); + return NULL; } } p = skipwhite(p); if (argcount == MAX_FUNC_ARGS) { - emsg(_(e_too_many_argument_types)); - return &t_any; + if (give_error) + emsg(_(e_too_many_argument_types)); + return NULL; } } p = skipwhite(p); if (*p != ')') { - emsg(_(e_missing_close)); - return &t_any; + if (give_error) + emsg(_(e_missing_close)); + return NULL; } *arg = p + 1; } @@ -765,10 +786,12 @@ parse_type(char_u **arg, garray_T *type_ { // parse return type ++*arg; - if (!VIM_ISWHITE(**arg)) + if (!VIM_ISWHITE(**arg) && give_error) semsg(_(e_white_space_required_after_str), ":"); *arg = skipwhite(*arg); - ret_type = parse_type(arg, type_gap); + ret_type = parse_type(arg, type_gap, give_error); + if (ret_type == NULL) + return NULL; } if (flags == 0 && first_optional == -1 && argcount <= 0) type = get_func_type(ret_type, argcount, type_gap); @@ -783,7 +806,7 @@ parse_type(char_u **arg, garray_T *type_ ? argcount : first_optional; if (func_type_add_arg_types(type, argcount, type_gap) == FAIL) - return &t_any; + return NULL; mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); } @@ -802,7 +825,8 @@ parse_type(char_u **arg, garray_T *type_ if (len == 4 && STRNCMP(*arg, "list", len) == 0) { *arg += len; - return parse_type_member(arg, &t_list_any, type_gap); + return parse_type_member(arg, &t_list_any, + type_gap, give_error); } break; case 'n': @@ -828,8 +852,9 @@ parse_type(char_u **arg, garray_T *type_ break; } - semsg(_(e_type_not_recognized_str), *arg); - return &t_any; + if (give_error) + semsg(_(e_type_not_recognized_str), *arg); + return NULL; } /* @@ -1016,9 +1041,12 @@ vartype_name(vartype_T type) char * type_name(type_T *type, char **tofree) { - char *name = vartype_name(type->tt_type); + char *name; *tofree = NULL; + if (type == NULL) + return "[unknown]"; + name = vartype_name(type->tt_type); if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) { char *member_free;