To: vim_dev@googlegroups.com Subject: Patch 8.2.0675 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0675 Problem: Vim9: no support for closures. Solution: Do not re-use stack entries. Files: src/vim9compile.c, src/ex_docmd.c, src/proto/ex_docmd.pro, src/evalvars.c, src/proto/evalvars.pro *** ../vim-8.2.0674/src/vim9compile.c 2020-04-28 22:49:04.592008615 +0200 --- src/vim9compile.c 2020-05-01 15:33:54.721981415 +0200 *************** *** 97,102 **** --- 97,103 ---- 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,118 **** 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 garray_T ctx_imports; // imported items --- 113,119 ---- garray_T ctx_instr; // generated instructions garray_T ctx_locals; // currently visible local variables ! int ctx_locals_count; // total number of local variables garray_T ctx_imports; // imported items *************** *** 120,125 **** --- 121,129 ---- // 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,158 **** static int check_type(type_T *expected, type_T *actual, int give_msg); /* ! * Lookup variable "name" in the local scope and return the index. */ ! static int lookup_local(char_u *name, size_t len, cctx_T *cctx) { int idx; if (len == 0) ! return -1; 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; } /* --- 139,163 ---- static int check_type(type_T *expected, type_T *actual, int give_msg); /* ! * Lookup variable "name" in the local scope and return it. ! * Return NULL if not found. */ ! static lvar_T * lookup_local(char_u *name, size_t len, cctx_T *cctx) { int idx; if (len == 0) ! 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 lvar; } ! return NULL; } /* *************** *** 217,223 **** { if (lookup_script(p, len) == OK || (cctx != NULL ! && (lookup_local(p, len, cctx) >= 0 || find_imported(p, len, cctx) != NULL))) { semsg("E1073: imported name already defined: %s", p); --- 222,228 ---- { if (lookup_script(p, len) == OK || (cctx != NULL ! && (lookup_local(p, len, cctx) != NULL || find_imported(p, len, cctx) != NULL))) { semsg("E1073: imported name already defined: %s", p); *************** *** 1458,1490 **** /* * Reserve space for a local variable. ! * Return the index or -1 if it failed. */ ! static int 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; } 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; lvar->lv_name = vim_strnsave(name, (int)(len == 0 ? STRLEN(name) : len)); lvar->lv_const = isConst; lvar->lv_type = type; ! return idx; } /* --- 1463,1496 ---- /* * Reserve space for a local variable. ! * Return the variable or NULL if it failed. */ ! static lvar_T * reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type) { 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 NULL; } if (ga_grow(&cctx->ctx_locals, 1) == FAIL) ! 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 lvar; } /* *************** *** 1511,1517 **** * Free all local variables. */ static void ! free_local(cctx_T *cctx) { unwind_locals(cctx, 0); ga_clear(&cctx->ctx_locals); --- 1517,1523 ---- * Free all local variables. */ static void ! free_locals(cctx_T *cctx) { unwind_locals(cctx, 0); ga_clear(&cctx->ctx_locals); *************** *** 2331,2340 **** } else { ! idx = lookup_local(*arg, len, cctx); ! if (idx >= 0) { ! type = (((lvar_T *)cctx->ctx_locals.ga_data) + idx)->lv_type; gen_load = TRUE; } else --- 2337,2348 ---- } else { ! lvar_T *lvar = lookup_local(*arg, len, cctx); ! ! if (lvar != NULL) { ! type = lvar->lv_type; ! idx = lvar->lv_idx; gen_load = TRUE; } else *************** *** 4040,4046 **** 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; --- 4048,4053 ---- *************** *** 4050,4056 **** int oplen = 0; int heredoc = FALSE; type_T *type = &t_any; ! lvar_T *lvar; char_u *name; char_u *sp; int has_type = FALSE; --- 4057,4063 ---- int oplen = 0; int heredoc = FALSE; type_T *type = &t_any; ! lvar_T *lvar = NULL; char_u *name; char_u *sp; int has_type = FALSE; *************** *** 4203,4208 **** --- 4210,4217 ---- } else { + int idx; + for (idx = 0; reserved[idx] != NULL; ++idx) if (STRCMP(reserved[idx], name) == 0) { *************** *** 4210,4217 **** goto theend; } ! idx = lookup_local(arg, varlen, cctx); ! if (idx >= 0) { if (is_decl) { --- 4219,4226 ---- goto theend; } ! lvar = lookup_local(arg, varlen, cctx); ! if (lvar != NULL) { if (is_decl) { *************** *** 4220,4229 **** } else { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (lvar->lv_const) { ! semsg(_("E1018: Cannot assign to a constant: %s"), name); goto theend; } } --- 4229,4238 ---- } else { if (lvar->lv_const) { ! semsg(_("E1018: Cannot assign to a constant: %s"), ! name); goto theend; } } *************** *** 4262,4272 **** type = parse_type(&p, cctx->ctx_type_list); has_type = TRUE; } ! else if (idx >= 0) ! { ! lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; type = lvar->lv_type; - } } sp = p; --- 4271,4278 ---- type = parse_type(&p, cctx->ctx_type_list); has_type = TRUE; } ! else if (lvar != NULL) type = lvar->lv_type; } sp = p; *************** *** 4288,4294 **** goto theend; } ! if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE) { if (oplen > 1 && !heredoc) { --- 4294,4300 ---- goto theend; } ! if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE) { if (oplen > 1 && !heredoc) { *************** *** 4301,4308 **** // 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) goto theend; new_local = TRUE; } --- 4307,4314 ---- // new local variable if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE)) goto theend; ! lvar = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type); ! if (lvar == NULL) goto theend; new_local = TRUE; } *************** *** 4370,4376 **** generate_LOADV(cctx, name + 2, TRUE); break; case dest_local: ! generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); break; } } --- 4376,4382 ---- generate_LOADV(cctx, name + 2, TRUE); break; case dest_local: ! generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); break; } } *************** *** 4392,4400 **** 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)) { - lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) --- 4398,4405 ---- stack = &cctx->ctx_type_stack; stacktype = stack->ga_len == 0 ? &t_void : ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! if (lvar != NULL && (is_decl || !has_type)) { if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) *************** *** 4546,4551 **** --- 4551,4557 ---- char_u *rawname = name + (name[1] == ':' ? 2 : 0); imported_T *import = NULL; int sid = current_sctx.sc_sid; + int idx; if (name[1] != ':') { *************** *** 4581,4586 **** --- 4587,4593 ---- } break; case dest_local: + if (lvar != NULL) { isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; *************** *** 4593,4605 **** garray_T *stack = &cctx->ctx_type_stack; isn->isn_type = ISN_STORENR; ! isn->isn_arg.storenr.stnr_idx = idx; isn->isn_arg.storenr.stnr_val = val; if (stack->ga_len > 0) --stack->ga_len; } else ! generate_STORE(cctx, ISN_STORE, idx, NULL); } break; } --- 4600,4612 ---- garray_T *stack = &cctx->ctx_type_stack; isn->isn_type = ISN_STORENR; ! 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, lvar->lv_idx, NULL); } break; } *************** *** 5283,5290 **** 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" type_T *vartype; // TODO: list of variables: "for [key, value] in dict" --- 5290,5297 ---- garray_T *instr = &cctx->ctx_instr; garray_T *stack = &cctx->ctx_type_stack; scope_T *scope; ! 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,5299 **** for (p = arg; eval_isnamec1(*p); ++p) ; varlen = p - arg; ! var_idx = lookup_local(arg, varlen, cctx); ! if (var_idx >= 0) { semsg(_("E1023: variable already defined: %s"), arg); return NULL; --- 5299,5306 ---- for (p = arg; eval_isnamec1(*p); ++p) ; varlen = p - arg; ! var_lvar = lookup_local(arg, varlen, cctx); ! if (var_lvar != NULL) { semsg(_("E1023: variable already defined: %s"), arg); return NULL; *************** *** 5314,5336 **** 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 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) { drop_scope(cctx); return NULL; } ! generate_STORENR(cctx, loop_idx, -1); // compile "expr", it remains on the stack until "endfor" arg = p; --- 5321,5344 ---- return NULL; // Reserve a variable to store the loop iteration counter. ! 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_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_lvar->lv_idx, -1); // compile "expr", it remains on the stack until "endfor" arg = p; *************** *** 5349,5365 **** 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; ! } // "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); return arg; } --- 5357,5369 ---- return NULL; } if (vartype->tt_member->tt_type != VAR_ANY) ! 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_lvar->lv_idx); ! generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); return arg; } *************** *** 6158,6164 **** || *ea.cmd == '$' || *ea.cmd == '@' || ((p - ea.cmd) > 2 && ea.cmd[1] == ':') ! || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0 || lookup_script(ea.cmd, p - ea.cmd) == OK || find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL) { --- 6162,6168 ---- || *ea.cmd == '$' || *ea.cmd == '@' || ((p - ea.cmd) > 2 && ea.cmd[1] == ':') ! || 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,6181 **** * COMMAND after range */ ea.cmd = skip_range(ea.cmd, NULL); ! p = find_ex_command(&ea, NULL, is_ex_command ? NULL : lookup_local, &cctx); if (p == ea.cmd && ea.cmdidx != CMD_SIZE) --- 6179,6186 ---- * COMMAND after range */ ea.cmd = skip_range(ea.cmd, NULL); ! 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,6355 **** dfunc->df_deleted = FALSE; dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; ! dfunc->df_varcount = cctx.ctx_max_local; } { --- 6354,6360 ---- dfunc->df_deleted = FALSE; dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; ! dfunc->df_varcount = cctx.ctx_locals_count; } { *************** *** 6431,6437 **** current_sctx = save_current_sctx; free_imported(&cctx); ! free_local(&cctx); ga_clear(&cctx.ctx_type_stack); } --- 6436,6442 ---- current_sctx = save_current_sctx; free_imported(&cctx); ! free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); } *** ../vim-8.2.0674/src/ex_docmd.c 2020-04-30 22:29:36.622024155 +0200 --- src/ex_docmd.c 2020-05-01 15:30:01.390841317 +0200 *************** *** 3157,3163 **** find_ex_command( exarg_T *eap, int *full UNUSED, ! int (*lookup)(char_u *, size_t, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; --- 3157,3163 ---- find_ex_command( exarg_T *eap, int *full UNUSED, ! void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; *************** *** 3197,3203 **** // "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) { eap->cmdidx = CMD_let; return eap->cmd; --- 3197,3203 ---- // "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) != NULL) { eap->cmdidx = CMD_let; return eap->cmd; *** ../vim-8.2.0674/src/proto/ex_docmd.pro 2020-04-16 22:10:42.656733692 +0200 --- src/proto/ex_docmd.pro 2020-05-01 15:27:47.867333219 +0200 *************** *** 7,13 **** 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); int modifier_len(char_u *cmd); int cmd_exists(char_u *name); cmdidx_T excmd_get_cmdidx(char_u *cmd, int len); --- 7,13 ---- 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, 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,25 **** 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); char_u *find_nextcmd(char_u *p); char_u *check_nextcmd(char_u *p); char_u *get_command_name(expand_T *xp, int idx); --- 19,25 ---- 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 *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); *** ../vim-8.2.0674/src/evalvars.c 2020-05-01 14:10:10.188390740 +0200 --- src/evalvars.c 2020-05-01 15:25:42.127796319 +0200 *************** *** 2495,2513 **** /* * Look for "name[len]" in script-local variables. ! * Return -1 when not found. */ ! int 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; hashitem_T *hi; if (ht == NULL) ! return -1; if (len < sizeof(buffer) - 1) { vim_strncpy(buffer, name, len); --- 2495,2513 ---- /* * Look for "name[len]" in script-local variables. ! * Return a non-NULL pointer when found, NULL when not found. */ ! 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; ! void *res; hashitem_T *hi; if (ht == NULL) ! return NULL; if (len < sizeof(buffer) - 1) { vim_strncpy(buffer, name, len); *************** *** 2517,2531 **** { p = vim_strnsave(name, (int)len); if (p == NULL) ! return -1; } hi = hash_find(ht, p); ! res = HASHITEM_EMPTY(hi) ? -1 : 1; // if not script-local, then perhaps imported ! if (res == -1 && find_imported(p, 0, NULL) != NULL) ! res = 1; if (p != buffer) vim_free(p); --- 2517,2531 ---- { p = vim_strnsave(name, (int)len); if (p == NULL) ! return NULL; } hi = hash_find(ht, p); ! res = HASHITEM_EMPTY(hi) ? NULL : hi; // if not script-local, then perhaps imported ! if (res == NULL && find_imported(p, 0, NULL) != NULL) ! res = p; if (p != buffer) vim_free(p); *** ../vim-8.2.0674/src/proto/evalvars.pro 2020-04-19 16:28:55.296495996 +0200 --- src/proto/evalvars.pro 2020-05-01 15:25:08.939918638 +0200 *************** *** 55,61 **** 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); 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); --- 55,61 ---- 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); ! 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); *** ../vim-8.2.0674/src/version.c 2020-05-01 14:26:17.132949262 +0200 --- src/version.c 2020-05-01 15:42:04.808364746 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 675, /**/ -- hundred-and-one symptoms of being an internet addict: 36. You miss more than five meals a week downloading the latest games from Apogee. /// 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 ///