# HG changeset patch # User Bram Moolenaar # Date 1607780705 -3600 # Node ID bb7531f7752959f4e276fc3b094300dfedeb8afb # Parent 586c5efd71b1088f85b4b14621f9f5e7210e620e patch 8.2.2131: Vim9: crash when lambda uses same var as assignment Commit: https://github.com/vim/vim/commit/709664cca0b59b69caa1ed40ebfcf00b2c672693 Author: Bram Moolenaar Date: Sat Dec 12 14:33:41 2020 +0100 patch 8.2.2131: Vim9: crash when lambda uses same var as assignment 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) diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2721,19 +2721,23 @@ get_script_local_ht(void) /* * Look for "name[len]" in script-local variables. - * Return a non-NULL pointer when found, NULL when not found. + * Return OK when found, FAIL when not found. */ - void * -lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED) + 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; - void *res; + int res; hashitem_T *hi; if (ht == NULL) - return NULL; + return FAIL; if (len < sizeof(buffer) - 1) { // avoid an alloc/free for short names @@ -2744,20 +2748,19 @@ lookup_scriptvar(char_u *name, size_t le { p = vim_strnsave(name, len); if (p == NULL) - return NULL; + return FAIL; } hi = hash_find(ht, p); - res = HASHITEM_EMPTY(hi) ? NULL : hi; + res = HASHITEM_EMPTY(hi) ? FAIL : OK; // if not script-local, then perhaps imported - if (res == NULL && find_imported(p, 0, NULL) != NULL) - res = p; + if (res == FAIL && find_imported(p, 0, NULL) != NULL) + res = OK; if (p != buffer) vim_free(p); - // Don't return "buffer", gcc complains. - return res == NULL ? NULL : IObuff; + return res; } /* diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3271,7 +3271,7 @@ skip_option_env_lead(char_u *start) find_ex_command( exarg_T *eap, int *full UNUSED, - void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED, + int (*lookup)(char_u *, size_t, void *, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; @@ -3387,7 +3387,7 @@ find_ex_command( || *eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@' - || lookup(eap->cmd, p - eap->cmd, cctx) != NULL) + || lookup(eap->cmd, p - eap->cmd, NULL, cctx) == OK) { eap->cmdidx = CMD_var; 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 @@ -59,7 +59,7 @@ 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); hashtab_T *get_script_local_ht(void); -void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy); +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); 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 @@ -13,7 +13,7 @@ void undo_cmdmod(cmdmod_T *cmod); 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); +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); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -481,6 +481,12 @@ def Test_call_lambda_args() 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 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 */ /**/ + 2131, +/**/ 2130, /**/ 2129, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -148,45 +148,51 @@ struct cctx_S { 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) + * 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 *lvar; + lvar_T *lvp; if (len == 0) - return NULL; + return FAIL; // 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; + 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) { - 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; + 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,7 +383,7 @@ check_defined(char_u *p, size_t len, cct p[len] = NUL; if (script_var_exists(p, len, FALSE, cctx) == OK || (cctx != NULL - && (lookup_local(p, len, 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,13 +2561,13 @@ compile_load( } else { - lvar_T *lvar = lookup_local(*arg, len, cctx); - - if (lvar != NULL) + lvar_T lvar; + + if (lookup_local(*arg, len, &lvar, cctx) == OK) { - type = lvar->lv_type; - idx = lvar->lv_idx; - if (lvar->lv_from_outer) + type = lvar.lv_type; + idx = lvar.lv_idx; + if (lvar.lv_from_outer) gen_load_outer = TRUE; else gen_load = TRUE; @@ -2763,7 +2769,7 @@ compile_call( // An argument or local variable can be a function reference, this // overrules a function name. - if (lookup_local(namebuf, varlen, cctx) == NULL + 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,6 +5372,7 @@ compile_assignment(char_u *arg, exarg_T 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,8 +5431,10 @@ compile_assignment(char_u *arg, exarg_T goto theend; } - lvar = lookup_local(var_start, varlen, cctx); - if (lvar == NULL) + + 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,8 +6588,7 @@ compile_for(char_u *arg_start, cctx_T *c } else { - var_lvar = lookup_local(arg, varlen, cctx); - if (var_lvar != NULL) + if (lookup_local(arg, varlen, NULL, cctx) == OK) { semsg(_(e_variable_already_declared), arg); goto failed; @@ -7584,7 +7592,7 @@ compile_def_function(ufunc_T *ufunc, int || *ea.cmd == '$' || *ea.cmd == '@' || ((len) > 2 && ea.cmd[1] == ':') - || lookup_local(ea.cmd, len, &cctx) != NULL + || 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,7 +7645,7 @@ compile_def_function(ufunc_T *ufunc, int } } p = find_ex_command(&ea, NULL, starts_with_colon ? NULL - : (void *(*)(char_u *, size_t, cctx_T *))lookup_local, + : (int (*)(char_u *, size_t, void *, cctx_T *))lookup_local, &cctx); if (p == ea.cmd && ea.cmdidx != CMD_SIZE)