Mercurial > vim
diff src/vim9compile.c @ 20244:23d75968ca5e
patch 8.2.0677: Vim9: no support for closures
Commit: https://github.com/vim/vim/commit/c8cd2b34d1027c93fbca90f3cdc8123fe22dfa25
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri May 1 19:29:08 2020 +0200
patch 8.2.0677: Vim9: no support for closures
Problem: Vim9: no support for closures.
Solution: Find variables in the outer function scope, so long as the scope
exists.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 01 May 2020 19:30:04 +0200 |
parents | 2135b4641680 |
children | e46e72aaff74 |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -97,9 +97,10 @@ struct scope_S { typedef struct { char_u *lv_name; type_T *lv_type; - int lv_idx; // index of the variable on the stack - int lv_const; // when TRUE cannot be assigned to - int lv_arg; // when TRUE this is an argument + int lv_idx; // index of the variable on the stack + int lv_from_outer; // when TRUE using ctx_outer scope + int lv_const; // when TRUE cannot be assigned to + int lv_arg; // when TRUE this is an argument } lvar_T; /* @@ -123,6 +124,7 @@ struct cctx_S { cctx_T *ctx_outer; // outer scope for lambda or nested // function + int ctx_outer_used; // var in ctx_outer was used garray_T ctx_type_stack; // type of each item on the stack garray_T *ctx_type_list; // list of pointers to allocated types @@ -146,17 +148,37 @@ static int check_type(type_T *expected, lookup_local(char_u *name, size_t len, cctx_T *cctx) { int idx; + lvar_T *lvar; if (len == 0) return NULL; + + // Find local in current function scope. for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx) { - lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; - + lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (STRNCMP(name, lvar->lv_name, len) == 0 && STRLEN(lvar->lv_name) == len) + { + lvar->lv_from_outer = FALSE; return lvar; - } + } + } + + // Find local in outer function scope. + if (cctx->ctx_outer != NULL) + { + lvar = lookup_local(name, len, cctx->ctx_outer); + if (lvar != NULL) + { + // TODO: are there situations we should not mark the outer scope as + // used? + cctx->ctx_outer_used = TRUE; + lvar->lv_from_outer = TRUE; + return lvar; + } + } + return NULL; } @@ -417,6 +439,71 @@ typval2type(typval_T *tv) return &t_any; // not used } + static void +type_mismatch(type_T *expected, type_T *actual) +{ + char *tofree1, *tofree2; + + semsg(_("E1013: type mismatch, expected %s but got %s"), + type_name(expected, &tofree1), type_name(actual, &tofree2)); + vim_free(tofree1); + vim_free(tofree2); +} + + static void +arg_type_mismatch(type_T *expected, type_T *actual, int argidx) +{ + char *tofree1, *tofree2; + + semsg(_("E1013: argument %d: type mismatch, expected %s but got %s"), + argidx, + type_name(expected, &tofree1), type_name(actual, &tofree2)); + vim_free(tofree1); + vim_free(tofree2); +} + +/* + * Check if the expected and actual types match. + * Does not allow for assigning "any" to a specific type. + */ + static int +check_type(type_T *expected, type_T *actual, int give_msg) +{ + int ret = OK; + + // When expected is "unknown" we accept any actual type. + // When expected is "any" we accept any actual type except "void". + if (expected->tt_type != VAR_UNKNOWN + && !(expected->tt_type == VAR_ANY && actual->tt_type != VAR_VOID)) + + { + if (expected->tt_type != actual->tt_type) + { + if (give_msg) + type_mismatch(expected, actual); + return FAIL; + } + if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST) + { + // "unknown" is used for an empty list or dict + if (actual->tt_member != &t_unknown) + ret = check_type(expected->tt_member, actual->tt_member, FALSE); + } + else if (expected->tt_type == VAR_FUNC) + { + if (expected->tt_member != &t_unknown) + 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 ret; +} + ///////////////////////////////////////////////////////////////////// // Following generate_ functions expect the caller to call ga_grow(). @@ -740,6 +827,29 @@ generate_TYPECHECK(cctx_T *cctx, type_T } /* + * Check that + * - "actual" is "expected" type or + * - "actual" is a type that can be "expected" type: add a runtime check; or + * - return FAIL. + */ + static int +need_type(type_T *actual, type_T *expected, int offset, cctx_T *cctx) +{ + if (check_type(expected, actual, FALSE) == OK) + return OK; + if (actual->tt_type != VAR_ANY + && actual->tt_type != VAR_UNKNOWN + && !(actual->tt_type == VAR_FUNC + && (actual->tt_member == &t_any || actual->tt_argcount < 0))) + { + type_mismatch(expected, actual); + return FAIL; + } + generate_TYPECHECK(cctx, expected, offset); + return OK; +} + +/* * Generate an ISN_PUSHNR instruction. */ static int @@ -1272,7 +1382,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu else expected = ufunc->uf_va_type->tt_member; actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i]; - if (check_type(expected, actual, FALSE) == FAIL) + if (need_type(actual, expected, -argcount + i, cctx) == FAIL) { arg_type_mismatch(expected, actual, i + 1); return FAIL; @@ -1543,6 +1653,20 @@ skip_type(char_u *start) if (*p == '>') ++p; } + else if (*p == '(' && STRNCMP("func", start, 4) == 0) + { + // handle func(args): type + ++p; + while (*p != ')' && *p != NUL) + { + p = skip_type(p); + if (*p == ',') + p = skipwhite(p + 1); + } + if (*p == ')' && p[1] == ':') + p = skip_type(skipwhite(p + 2)); + } + return p; } @@ -2309,6 +2433,7 @@ compile_load(char_u **arg, char_u *end_a size_t len = end - *arg; int idx; int gen_load = FALSE; + int gen_load_outer = FALSE; name = vim_strnsave(*arg, end - *arg); if (name == NULL) @@ -2343,7 +2468,10 @@ compile_load(char_u **arg, char_u *end_a { type = lvar->lv_type; idx = lvar->lv_idx; - gen_load = TRUE; + if (lvar->lv_from_outer) + gen_load_outer = TRUE; + else + gen_load = TRUE; } else { @@ -2370,6 +2498,8 @@ compile_load(char_u **arg, char_u *end_a } if (gen_load) res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); + if (gen_load_outer) + res = generate_LOAD(cctx, ISN_LOADOUTER, idx, NULL, type); } *arg = end; @@ -2578,94 +2708,6 @@ to_name_const_end(char_u *arg) return p; } - static void -type_mismatch(type_T *expected, type_T *actual) -{ - char *tofree1, *tofree2; - - semsg(_("E1013: type mismatch, expected %s but got %s"), - type_name(expected, &tofree1), type_name(actual, &tofree2)); - vim_free(tofree1); - vim_free(tofree2); -} - - static void -arg_type_mismatch(type_T *expected, type_T *actual, int argidx) -{ - char *tofree1, *tofree2; - - semsg(_("E1013: argument %d: type mismatch, expected %s but got %s"), - argidx, - type_name(expected, &tofree1), type_name(actual, &tofree2)); - vim_free(tofree1); - vim_free(tofree2); -} - -/* - * Check if the expected and actual types match. - * Does not allow for assigning "any" to a specific type. - */ - static int -check_type(type_T *expected, type_T *actual, int give_msg) -{ - int ret = OK; - - // When expected is "unknown" we accept any actual type. - // When expected is "any" we accept any actual type except "void". - if (expected->tt_type != VAR_UNKNOWN - && !(expected->tt_type == VAR_ANY && actual->tt_type != VAR_VOID)) - - { - if (expected->tt_type != actual->tt_type) - { - if (give_msg) - type_mismatch(expected, actual); - return FAIL; - } - if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST) - { - // "unknown" is used for an empty list or dict - if (actual->tt_member != &t_unknown) - ret = check_type(expected->tt_member, actual->tt_member, FALSE); - } - else if (expected->tt_type == VAR_FUNC) - { - if (expected->tt_member != &t_unknown) - 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 ret; -} - -/* - * Check that - * - "actual" is "expected" type or - * - "actual" is a type that can be "expected" type: add a runtime check; or - * - return FAIL. - */ - static int -need_type(type_T *actual, type_T *expected, int offset, cctx_T *cctx) -{ - if (check_type(expected, actual, FALSE) == OK) - return OK; - if (actual->tt_type != VAR_ANY - && actual->tt_type != VAR_UNKNOWN - && !(actual->tt_type == VAR_FUNC - && (actual->tt_member == &t_any || actual->tt_argcount < 0))) - { - type_mismatch(expected, actual); - return FAIL; - } - generate_TYPECHECK(cctx, expected, offset); - return OK; -} - /* * parse a list: [expr, expr] * "*arg" points to the '['. @@ -2734,7 +2776,7 @@ compile_lambda(char_u **arg, cctx_T *cct // The function will have one line: "return {expr}". // Compile it into instructions. - compile_def_function(ufunc, TRUE); + compile_def_function(ufunc, TRUE, cctx); if (ufunc->uf_dfunc_idx >= 0) { @@ -2779,7 +2821,7 @@ compile_lambda_call(char_u **arg, cctx_T // The function will have one line: "return {expr}". // Compile it into instructions. - compile_def_function(ufunc, TRUE); + compile_def_function(ufunc, TRUE, cctx); // compile the arguments *arg = skipwhite(*arg + 1); @@ -4227,14 +4269,10 @@ compile_assignment(char_u *arg, exarg_T semsg(_("E1017: Variable already declared: %s"), name); goto theend; } - else + else if (lvar->lv_const) { - if (lvar->lv_const) - { - semsg(_("E1018: Cannot assign to a constant: %s"), - name); - goto theend; - } + semsg(_("E1018: Cannot assign to a constant: %s"), name); + goto theend; } } else if (STRNCMP(arg, "s:", 2) == 0 @@ -5931,11 +5969,12 @@ theend: * 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). + * "outer_cctx" is set for a nested function. * This can be used recursively through compile_lambda(), which may reallocate * "def_functions". */ void -compile_def_function(ufunc_T *ufunc, int set_return_type) +compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) { char_u *line = NULL; char_u *p; @@ -5976,6 +6015,7 @@ compile_def_function(ufunc_T *ufunc, int CLEAR_FIELD(cctx); cctx.ctx_ufunc = ufunc; cctx.ctx_lnum = -1; + cctx.ctx_outer = outer_cctx; ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10); ga_init2(&cctx.ctx_type_stack, sizeof(type_T *), 50); ga_init2(&cctx.ctx_imports, sizeof(imported_T), 10); @@ -6355,6 +6395,8 @@ compile_def_function(ufunc_T *ufunc, int dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; dfunc->df_varcount = cctx.ctx_locals_count; + if (cctx.ctx_outer_used) + ufunc->uf_flags |= FC_CLOSURE; } { @@ -6533,6 +6575,7 @@ delete_instr(isn_T *isn) case ISN_INDEX: case ISN_JUMP: case ISN_LOAD: + case ISN_LOADOUTER: case ISN_LOADSCRIPT: case ISN_LOADREG: case ISN_LOADV: