# HG changeset patch # User Bram Moolenaar # Date 1588340704 -7200 # Node ID 2135b464168084b9e5fcc8298cfeab07ca15ba72 # Parent d3fee66f8f3ecc647d9846c041f8393aa26602e3 patch 8.2.0675: Vim9: no support for closures Commit: https://github.com/vim/vim/commit/b84a381c75e50ca0e0a24cc3e152d0c70f8c2c7d Author: Bram Moolenaar Date: Fri May 1 15:44:29 2020 +0200 patch 8.2.0675: Vim9: no support for closures Problem: Vim9: no support for closures. Solution: Do not re-use stack entries. diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2495,19 +2495,19 @@ get_script_local_ht(void) /* * Look for "name[len]" in script-local variables. - * Return -1 when not found. + * Return a non-NULL pointer when found, NULL when not found. */ - int + void * lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED) { hashtab_T *ht = get_script_local_ht(); char_u buffer[30]; char_u *p; - int res; + void *res; hashitem_T *hi; if (ht == NULL) - return -1; + return NULL; if (len < sizeof(buffer) - 1) { vim_strncpy(buffer, name, len); @@ -2517,15 +2517,15 @@ lookup_scriptvar(char_u *name, size_t le { p = vim_strnsave(name, (int)len); if (p == NULL) - return -1; + return NULL; } hi = hash_find(ht, p); - res = HASHITEM_EMPTY(hi) ? -1 : 1; + res = HASHITEM_EMPTY(hi) ? NULL : hi; // if not script-local, then perhaps imported - if (res == -1 && find_imported(p, 0, NULL) != NULL) - res = 1; + if (res == NULL && find_imported(p, 0, NULL) != NULL) + res = p; if (p != buffer) vim_free(p); diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3157,7 +3157,7 @@ append_command(char_u *cmd) find_ex_command( exarg_T *eap, int *full UNUSED, - int (*lookup)(char_u *, size_t, cctx_T *) UNUSED, + void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; @@ -3197,7 +3197,7 @@ find_ex_command( // "g:var = expr" // "var = expr" where "var" is a local var name. if (((p - eap->cmd) > 2 && eap->cmd[1] == ':') - || lookup(eap->cmd, p - eap->cmd, cctx) >= 0) + || lookup(eap->cmd, p - eap->cmd, cctx) != NULL) { eap->cmdidx = CMD_let; return eap->cmd; diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -55,7 +55,7 @@ int get_var_tv(char_u *name, int len, ty void check_vars(char_u *name, int len); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); -int lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy); +void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy); hashtab_T *find_var_ht(char_u *name, char_u **varname); char_u *get_var_value(char_u *name); void new_script_vars(scid_T id); diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -7,7 +7,7 @@ void *getline_cookie(char_u *(*fgetline) int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only); int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); -char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx); +char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx); int modifier_len(char_u *cmd); int cmd_exists(char_u *name); cmdidx_T excmd_get_cmdidx(char_u *cmd, int len); @@ -19,7 +19,7 @@ void separate_nextcmd(exarg_T *eap); char_u *skip_cmd_arg(char_u *p, int rembs); int get_bad_opt(char_u *p, exarg_T *eap); int ends_excmd(int c); -int ends_excmd2(char_u *before, char_u *cmd); +int ends_excmd2(char_u *cmd_start, char_u *cmd); char_u *find_nextcmd(char_u *p); char_u *check_nextcmd(char_u *p); char_u *get_command_name(expand_T *xp, int idx); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 675, +/**/ 674, /**/ 673, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -97,6 +97,7 @@ 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 } lvar_T; @@ -112,7 +113,7 @@ struct cctx_S { garray_T ctx_instr; // generated instructions garray_T ctx_locals; // currently visible local variables - int ctx_max_local; // maximum number of locals at one time + int ctx_locals_count; // total number of local variables garray_T ctx_imports; // imported items @@ -120,6 +121,9 @@ struct cctx_S { // commands after "else" scope_T *ctx_scope; // current scope, NULL at toplevel + cctx_T *ctx_outer; // outer scope for lambda or nested + // function + garray_T ctx_type_stack; // type of each item on the stack garray_T *ctx_type_list; // list of pointers to allocated types }; @@ -135,24 +139,25 @@ static void arg_type_mismatch(type_T *ex static int check_type(type_T *expected, type_T *actual, int give_msg); /* - * Lookup variable "name" in the local scope and return the index. + * Lookup variable "name" in the local scope and return it. + * Return NULL if not found. */ - static int + static lvar_T * lookup_local(char_u *name, size_t len, cctx_T *cctx) { int idx; if (len == 0) - return -1; + return NULL; for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx) { lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (STRNCMP(name, lvar->lv_name, len) == 0 && STRLEN(lvar->lv_name) == len) - return idx; - } - return -1; + return lvar; + } + return NULL; } /* @@ -217,7 +222,7 @@ check_defined(char_u *p, int len, cctx_T { if (lookup_script(p, len) == OK || (cctx != NULL - && (lookup_local(p, len, cctx) >= 0 + && (lookup_local(p, len, cctx) != NULL || find_imported(p, len, cctx) != NULL))) { semsg("E1073: imported name already defined: %s", p); @@ -1458,33 +1463,34 @@ generate_EXECCONCAT(cctx_T *cctx, int co /* * Reserve space for a local variable. - * Return the index or -1 if it failed. + * Return the variable or NULL if it failed. */ - static int + static lvar_T * reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type) { - int idx; lvar_T *lvar; if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx)) { emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len); - return -1; + return NULL; } if (ga_grow(&cctx->ctx_locals, 1) == FAIL) - return -1; - idx = cctx->ctx_locals.ga_len; - if (cctx->ctx_max_local < idx + 1) - cctx->ctx_max_local = idx + 1; - ++cctx->ctx_locals.ga_len; - - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; + return NULL; + lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + cctx->ctx_locals.ga_len++; + + // Every local variable uses the next entry on the stack. We could re-use + // the last ones when leaving a scope, but then variables used in a closure + // might get overwritten. To keep things simple do not re-use stack + // entries. This is less efficient, but memory is cheap these days. + lvar->lv_idx = cctx->ctx_locals_count++; + lvar->lv_name = vim_strnsave(name, (int)(len == 0 ? STRLEN(name) : len)); lvar->lv_const = isConst; lvar->lv_type = type; - return idx; + return lvar; } /* @@ -1511,7 +1517,7 @@ unwind_locals(cctx_T *cctx, int new_top) * Free all local variables. */ static void -free_local(cctx_T *cctx) +free_locals(cctx_T *cctx) { unwind_locals(cctx, 0); ga_clear(&cctx->ctx_locals); @@ -2331,10 +2337,12 @@ compile_load(char_u **arg, char_u *end_a } else { - idx = lookup_local(*arg, len, cctx); - if (idx >= 0) + lvar_T *lvar = lookup_local(*arg, len, cctx); + + if (lvar != NULL) { - type = (((lvar_T *)cctx->ctx_locals.ga_data) + idx)->lv_type; + type = lvar->lv_type; + idx = lvar->lv_idx; gen_load = TRUE; } else @@ -4040,7 +4048,6 @@ compile_assignment(char_u *arg, exarg_T int semicolon = 0; size_t varlen; garray_T *instr = &cctx->ctx_instr; - int idx = -1; int new_local = FALSE; char_u *op; int opt_type; @@ -4050,7 +4057,7 @@ compile_assignment(char_u *arg, exarg_T int oplen = 0; int heredoc = FALSE; type_T *type = &t_any; - lvar_T *lvar; + lvar_T *lvar = NULL; char_u *name; char_u *sp; int has_type = FALSE; @@ -4203,6 +4210,8 @@ compile_assignment(char_u *arg, exarg_T } else { + int idx; + for (idx = 0; reserved[idx] != NULL; ++idx) if (STRCMP(reserved[idx], name) == 0) { @@ -4210,8 +4219,8 @@ compile_assignment(char_u *arg, exarg_T goto theend; } - idx = lookup_local(arg, varlen, cctx); - if (idx >= 0) + lvar = lookup_local(arg, varlen, cctx); + if (lvar != NULL) { if (is_decl) { @@ -4220,10 +4229,10 @@ compile_assignment(char_u *arg, exarg_T } else { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (lvar->lv_const) { - semsg(_("E1018: Cannot assign to a constant: %s"), name); + semsg(_("E1018: Cannot assign to a constant: %s"), + name); goto theend; } } @@ -4262,11 +4271,8 @@ compile_assignment(char_u *arg, exarg_T type = parse_type(&p, cctx->ctx_type_list); has_type = TRUE; } - else if (idx >= 0) - { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; + else if (lvar != NULL) type = lvar->lv_type; - } } sp = p; @@ -4288,7 +4294,7 @@ compile_assignment(char_u *arg, exarg_T goto theend; } - if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE) + if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE) { if (oplen > 1 && !heredoc) { @@ -4301,8 +4307,8 @@ compile_assignment(char_u *arg, exarg_T // new local variable if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE)) goto theend; - idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type); - if (idx < 0) + lvar = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type); + if (lvar == NULL) goto theend; new_local = TRUE; } @@ -4370,7 +4376,7 @@ compile_assignment(char_u *arg, exarg_T generate_LOADV(cctx, name + 2, TRUE); break; case dest_local: - generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); + generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); break; } } @@ -4392,9 +4398,8 @@ compile_assignment(char_u *arg, exarg_T stack = &cctx->ctx_type_stack; stacktype = stack->ga_len == 0 ? &t_void : ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (idx >= 0 && (is_decl || !has_type)) + if (lvar != NULL && (is_decl || !has_type)) { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) @@ -4546,6 +4551,7 @@ compile_assignment(char_u *arg, exarg_T char_u *rawname = name + (name[1] == ':' ? 2 : 0); imported_T *import = NULL; int sid = current_sctx.sc_sid; + int idx; if (name[1] != ':') { @@ -4581,6 +4587,7 @@ compile_assignment(char_u *arg, exarg_T } break; case dest_local: + if (lvar != NULL) { isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; @@ -4593,13 +4600,13 @@ compile_assignment(char_u *arg, exarg_T garray_T *stack = &cctx->ctx_type_stack; isn->isn_type = ISN_STORENR; - isn->isn_arg.storenr.stnr_idx = idx; + isn->isn_arg.storenr.stnr_idx = lvar->lv_idx; isn->isn_arg.storenr.stnr_val = val; if (stack->ga_len > 0) --stack->ga_len; } else - generate_STORE(cctx, ISN_STORE, idx, NULL); + generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } break; } @@ -5283,8 +5290,8 @@ compile_for(char_u *arg, cctx_T *cctx) garray_T *instr = &cctx->ctx_instr; garray_T *stack = &cctx->ctx_type_stack; scope_T *scope; - int loop_idx; // index of loop iteration variable - int var_idx; // index of "var" + lvar_T *loop_lvar; // loop iteration variable + lvar_T *var_lvar; // variable for "var" type_T *vartype; // TODO: list of variables: "for [key, value] in dict" @@ -5292,8 +5299,8 @@ compile_for(char_u *arg, cctx_T *cctx) for (p = arg; eval_isnamec1(*p); ++p) ; varlen = p - arg; - var_idx = lookup_local(arg, varlen, cctx); - if (var_idx >= 0) + var_lvar = lookup_local(arg, varlen, cctx); + if (var_lvar != NULL) { semsg(_("E1023: variable already defined: %s"), arg); return NULL; @@ -5314,23 +5321,24 @@ compile_for(char_u *arg, cctx_T *cctx) return NULL; // Reserve a variable to store the loop iteration counter. - loop_idx = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number); - if (loop_idx < 0) - { - // only happens when out of memory + loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number); + if (loop_lvar == NULL) + { + // out of memory drop_scope(cctx); return NULL; } // Reserve a variable to store "var" - var_idx = reserve_local(cctx, arg, varlen, FALSE, &t_any); - if (var_idx < 0) - { + var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any); + if (var_lvar == NULL) + { + // out of memory or used as an argument drop_scope(cctx); return NULL; } - generate_STORENR(cctx, loop_idx, -1); + generate_STORENR(cctx, loop_lvar->lv_idx, -1); // compile "expr", it remains on the stack until "endfor" arg = p; @@ -5349,17 +5357,13 @@ compile_for(char_u *arg, cctx_T *cctx) return NULL; } if (vartype->tt_member->tt_type != VAR_ANY) - { - lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + var_idx; - - lvar->lv_type = vartype->tt_member; - } + var_lvar->lv_type = vartype->tt_member; // "for_end" is set when ":endfor" is found scope->se_u.se_for.fs_top_label = instr->ga_len; - generate_FOR(cctx, loop_idx); - generate_STORE(cctx, ISN_STORE, var_idx, NULL); + generate_FOR(cctx, loop_lvar->lv_idx); + generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); return arg; } @@ -6158,7 +6162,7 @@ compile_def_function(ufunc_T *ufunc, int || *ea.cmd == '$' || *ea.cmd == '@' || ((p - ea.cmd) > 2 && ea.cmd[1] == ':') - || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0 + || lookup_local(ea.cmd, p - ea.cmd, &cctx) != NULL || lookup_script(ea.cmd, p - ea.cmd) == OK || find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL) { @@ -6175,7 +6179,8 @@ compile_def_function(ufunc_T *ufunc, int * COMMAND after range */ ea.cmd = skip_range(ea.cmd, NULL); - p = find_ex_command(&ea, NULL, is_ex_command ? NULL : lookup_local, + p = find_ex_command(&ea, NULL, is_ex_command ? NULL + : (void *(*)(char_u *, size_t, cctx_T *))lookup_local, &cctx); if (p == ea.cmd && ea.cmdidx != CMD_SIZE) @@ -6349,7 +6354,7 @@ compile_def_function(ufunc_T *ufunc, int dfunc->df_deleted = FALSE; dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; - dfunc->df_varcount = cctx.ctx_max_local; + dfunc->df_varcount = cctx.ctx_locals_count; } { @@ -6431,7 +6436,7 @@ erret: current_sctx = save_current_sctx; free_imported(&cctx); - free_local(&cctx); + free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); }