To: vim_dev@googlegroups.com Subject: Patch 8.2.2131 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2131 Problem: Vim9: crash when lambda uses same var as assignment. Solution: Do not let lookup_local change lv_from_outer, make a copy. (closes #7461) Files: src/vim9compile.c, src/ex_docmd.c, src/proto/ex_docmd.pro, src/evalvars.c, src/proto/evalvars.pro, src/testdir/test_vim9_func.vim *** ../vim-8.2.2130/src/vim9compile.c 2020-12-10 19:43:36.629155311 +0100 --- src/vim9compile.c 2020-12-12 14:29:49.549661501 +0100 *************** *** 148,192 **** static void delete_def_function_contents(dfunc_T *dfunc); /* ! * 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; ! 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 = ((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; } /* --- 148,198 ---- static void delete_def_function_contents(dfunc_T *dfunc); /* ! * Lookup variable "name" in the local scope and return it in "lvar". ! * "lvar->lv_from_outer" is set accordingly. ! * If "lvar" is NULL only check if the variable can be found. ! * Return FAIL if not found. */ ! static int ! lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx) { int idx; ! lvar_T *lvp; if (len == 0) ! return FAIL; // Find local in current function scope. for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx) { ! lvp = ((lvar_T *)cctx->ctx_locals.ga_data) + idx; ! if (STRNCMP(name, lvp->lv_name, len) == 0 ! && STRLEN(lvp->lv_name) == len) { ! if (lvar != NULL) ! { ! *lvar = *lvp; ! lvar->lv_from_outer = FALSE; ! } ! return OK; } } // Find local in outer function scope. if (cctx->ctx_outer != NULL) { ! if (lookup_local(name, len, lvar, cctx->ctx_outer) == OK) { ! if (lvar != NULL) ! { ! cctx->ctx_outer_used = TRUE; ! lvar->lv_from_outer = TRUE; ! } ! return OK; } } ! return FAIL; } /* *************** *** 377,383 **** p[len] = NUL; if (script_var_exists(p, len, FALSE, cctx) == OK || (cctx != NULL ! && (lookup_local(p, len, cctx) != NULL || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) --- 383,389 ---- p[len] = NUL; if (script_var_exists(p, len, FALSE, cctx) == OK || (cctx != NULL ! && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) *************** *** 2555,2567 **** } else { ! lvar_T *lvar = lookup_local(*arg, len, cctx); ! if (lvar != NULL) { ! type = lvar->lv_type; ! idx = lvar->lv_idx; ! if (lvar->lv_from_outer) gen_load_outer = TRUE; else gen_load = TRUE; --- 2561,2573 ---- } else { ! lvar_T lvar; ! if (lookup_local(*arg, len, &lvar, cctx) == OK) { ! type = lvar.lv_type; ! idx = lvar.lv_idx; ! if (lvar.lv_from_outer) gen_load_outer = TRUE; else gen_load = TRUE; *************** *** 2763,2769 **** // An argument or local variable can be a function reference, this // overrules a function name. ! if (lookup_local(namebuf, varlen, cctx) == NULL && arg_exists(namebuf, varlen, NULL, NULL, NULL, cctx) != OK) { // If we can find the function by name generate the right call. --- 2769,2775 ---- // An argument or local variable can be a function reference, this // overrules a function name. ! if (lookup_local(namebuf, varlen, NULL, cctx) == FAIL && arg_exists(namebuf, varlen, NULL, NULL, NULL, cctx) != OK) { // If we can find the function by name generate the right call. *************** *** 5366,5371 **** --- 5372,5378 ---- assign_dest_T dest = dest_local; int opt_flags = 0; int vimvaridx = -1; + lvar_T local_lvar; lvar_T *lvar = NULL; lvar_T arg_lvar; int has_type = FALSE; *************** *** 5424,5431 **** goto theend; } ! lvar = lookup_local(var_start, varlen, cctx); ! if (lvar == NULL) { CLEAR_FIELD(arg_lvar); if (arg_exists(var_start, varlen, --- 5431,5440 ---- goto theend; } ! ! if (lookup_local(var_start, varlen, &local_lvar, cctx) == OK) ! lvar = &local_lvar; ! else { CLEAR_FIELD(arg_lvar); if (arg_exists(var_start, varlen, *************** *** 6579,6586 **** } else { ! var_lvar = lookup_local(arg, varlen, cctx); ! if (var_lvar != NULL) { semsg(_(e_variable_already_declared), arg); goto failed; --- 6588,6594 ---- } else { ! if (lookup_local(arg, varlen, NULL, cctx) == OK) { semsg(_(e_variable_already_declared), arg); goto failed; *************** *** 7584,7590 **** || *ea.cmd == '$' || *ea.cmd == '@' || ((len) > 2 && ea.cmd[1] == ':') ! || lookup_local(ea.cmd, len, &cctx) != NULL || arg_exists(ea.cmd, len, NULL, NULL, NULL, &cctx) == OK || script_var_exists(ea.cmd, len, --- 7592,7598 ---- || *ea.cmd == '$' || *ea.cmd == '@' || ((len) > 2 && ea.cmd[1] == ':') ! || lookup_local(ea.cmd, len, NULL, &cctx) == OK || arg_exists(ea.cmd, len, NULL, NULL, NULL, &cctx) == OK || script_var_exists(ea.cmd, len, *************** *** 7637,7643 **** } } p = find_ex_command(&ea, NULL, starts_with_colon ? NULL ! : (void *(*)(char_u *, size_t, cctx_T *))lookup_local, &cctx); if (p == ea.cmd && ea.cmdidx != CMD_SIZE) --- 7645,7651 ---- } } p = find_ex_command(&ea, NULL, starts_with_colon ? NULL ! : (int (*)(char_u *, size_t, void *, cctx_T *))lookup_local, &cctx); if (p == ea.cmd && ea.cmdidx != CMD_SIZE) *** ../vim-8.2.2130/src/ex_docmd.c 2020-12-11 19:30:26.789393285 +0100 --- src/ex_docmd.c 2020-12-12 14:06:22.938573143 +0100 *************** *** 3271,3277 **** find_ex_command( exarg_T *eap, int *full UNUSED, ! void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; --- 3271,3277 ---- find_ex_command( exarg_T *eap, int *full UNUSED, ! int (*lookup)(char_u *, size_t, void *, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; *************** *** 3387,3393 **** || *eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@' ! || lookup(eap->cmd, p - eap->cmd, cctx) != NULL) { eap->cmdidx = CMD_var; return eap->cmd; --- 3387,3393 ---- || *eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@' ! || lookup(eap->cmd, p - eap->cmd, NULL, cctx) == OK) { eap->cmdidx = CMD_var; return eap->cmd; *** ../vim-8.2.2130/src/proto/ex_docmd.pro 2020-11-25 20:09:05.509445589 +0100 --- src/proto/ex_docmd.pro 2020-12-12 14:07:13.978369210 +0100 *************** *** 13,19 **** int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); char_u *skip_option_env_lead(char_u *start); ! 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); --- 13,19 ---- int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); char_u *skip_option_env_lead(char_u *start); ! char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, void *, 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); *** ../vim-8.2.2130/src/evalvars.c 2020-11-30 17:40:41.299714396 +0100 --- src/evalvars.c 2020-12-12 14:22:52.263035573 +0100 *************** *** 2721,2739 **** /* * 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) { // avoid an alloc/free for short names --- 2721,2743 ---- /* * Look for "name[len]" in script-local variables. ! * Return OK when found, FAIL when not found. */ ! int ! lookup_scriptvar( ! char_u *name, ! size_t len, ! void *lvar UNUSED, ! 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 FAIL; if (len < sizeof(buffer) - 1) { // avoid an alloc/free for short names *************** *** 2744,2763 **** { p = vim_strnsave(name, 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); ! // Don't return "buffer", gcc complains. ! return res == NULL ? NULL : IObuff; } /* --- 2748,2766 ---- { p = vim_strnsave(name, len); if (p == NULL) ! return FAIL; } hi = hash_find(ht, p); ! res = HASHITEM_EMPTY(hi) ? FAIL : OK; // if not script-local, then perhaps imported ! if (res == FAIL && find_imported(p, 0, NULL) != NULL) ! res = OK; if (p != buffer) vim_free(p); ! return res; } /* *** ../vim-8.2.2130/src/proto/evalvars.pro 2020-10-10 19:07:04.187713866 +0200 --- src/proto/evalvars.pro 2020-12-12 14:10:23.613642282 +0100 *************** *** 59,65 **** 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); hashtab_T *get_script_local_ht(void); ! 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); --- 59,65 ---- 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); hashtab_T *get_script_local_ht(void); ! int lookup_scriptvar(char_u *name, size_t len, void *lvar, 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.2130/src/testdir/test_vim9_func.vim 2020-12-06 18:50:32.946220039 +0100 --- src/testdir/test_vim9_func.vim 2020-12-12 14:14:23.232774089 +0100 *************** *** 481,486 **** --- 481,492 ---- CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got string') enddef + def Test_lambda_uses_assigned_var() + CheckDefSuccess([ + 'var x: any = "aaa"' + 'x = filter(["bbb"], {_, v -> v =~ x})']) + enddef + " Default arg and varargs def MyDefVarargs(one: string, two = 'foo', ...rest: list): string var res = one .. ',' .. two *** ../vim-8.2.2130/src/version.c 2020-12-12 13:31:51.277403018 +0100 --- src/version.c 2020-12-12 14:28:18.697958507 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2131, /**/ -- Communication is one of the most compli..., eh, well, it's hard. You know what I mean. Not? /// 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 ///