To: vim_dev@googlegroups.com Subject: Patch 8.2.1846 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1846 Problem: Vim9: variables declared in a local block are not found in when a function is compiled. Solution: Look for script variables in sn_all_vars. Files: src/structs.h, src/vim9compile.c, src/proto/vim9compile.pro, src/userfunc.c, src/proto/userfunc.pro, src/ex_eval.c, src/vim9script.c, src/proto/vim9script.pro, src/vim9execute.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1845/src/structs.h 2020-10-14 19:39:16.041002546 +0200 --- src/structs.h 2020-10-14 22:23:00.902728717 +0200 *************** *** 1582,1587 **** --- 1582,1589 ---- char_u *uf_va_name; // name from "...name" or NULL type_T *uf_va_type; // type from "...name: type" or NULL type_T *uf_func_type; // type of the function, &t_func_any if unknown + int uf_block_depth; // nr of entries in uf_block_ids + int *uf_block_ids; // blocks a :def function is defined inside # if defined(FEAT_LUA) cfunc_T uf_cb; // callback function for cfunc cfunc_free_T uf_cb_free; // callback function to free cfunc *************** *** 1792,1798 **** garray_T sn_imports; // imported items, imported_T garray_T sn_type_list; // keeps types used by variables ! int sn_current_block_id; // Unique ID for each script block int sn_version; // :scriptversion int sn_had_command; // TRUE if any command was executed --- 1794,1801 ---- garray_T sn_imports; // imported items, imported_T garray_T sn_type_list; // keeps types used by variables ! int sn_current_block_id; // ID for current block, 0 for outer ! int sn_last_block_id; // Unique ID for each script block int sn_version; // :scriptversion int sn_had_command; // TRUE if any command was executed *** ../vim-8.2.1845/src/vim9compile.c 2020-10-12 22:07:09.631378864 +0200 --- src/vim9compile.c 2020-10-14 22:56:30.304517513 +0200 *************** *** 195,201 **** * Returns OK when found, FAIL otherwise. */ static int ! lookup_arg( char_u *name, size_t len, int *idxp, --- 195,201 ---- * Returns OK when found, FAIL otherwise. */ static int ! arg_exists( char_u *name, size_t len, int *idxp, *************** *** 247,253 **** if (cctx->ctx_outer != NULL) { // Lookup the name for an argument of the outer function. ! if (lookup_arg(name, len, idxp, type, gen_load_outer, cctx->ctx_outer) == OK) { *gen_load_outer = TRUE; --- 247,253 ---- if (cctx->ctx_outer != NULL) { // Lookup the name for an argument of the outer function. ! if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer) == OK) { *gen_load_outer = TRUE; *************** *** 259,264 **** --- 259,315 ---- } /* + * Lookup a script-local variable in the current script, possibly defined in a + * block that contains the function "cctx->ctx_ufunc". + * "cctx" is NULL at the script level. + * if "len" is <= 0 "name" must be NUL terminated. + * Return NULL when not found. + */ + static sallvar_T * + find_script_var(char_u *name, size_t len, cctx_T *cctx) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + hashitem_T *hi; + int cc; + sallvar_T *sav; + ufunc_T *ufunc; + + // Find the list of all script variables with the right name. + if (len > 0) + { + cc = name[len]; + name[len] = NUL; + } + hi = hash_find(&si->sn_all_vars.dv_hashtab, name); + if (len > 0) + name[len] = cc; + if (HASHITEM_EMPTY(hi)) + return NULL; + + sav = HI2SAV(hi); + if (sav->sav_block_id == 0 || cctx == NULL) + // variable defined in the script scope or not in a function. + return sav; + + // Go over the variables with this name and find one that was visible + // from the function. + ufunc = cctx->ctx_ufunc; + while (sav != NULL) + { + int idx; + + // Go over the blocks that this function was defined in. If the + // variable block ID matches it was visible to the function. + for (idx = 0; idx < ufunc->uf_block_depth; ++idx) + if (ufunc->uf_block_ids[idx] == sav->sav_block_id) + return sav; + sav = sav->sav_next; + } + + return NULL; + } + + /* * Returnd TRUE if the script context is Vim9 script. */ static int *************** *** 268,300 **** } /* ! * Lookup a variable in the current script. * If "vim9script" is TRUE the script must be Vim9 script. Used for "var" * without "s:". * Returns OK or FAIL. */ static int ! lookup_script(char_u *name, size_t len, int vim9script) { ! int cc; ! hashtab_T *ht; ! dictitem_T *di; if (current_sctx.sc_sid <= 0) return FAIL; ! ht = &SCRIPT_VARS(current_sctx.sc_sid); ! if (vim9script && !script_is_vim9()) return FAIL; ! cc = name[len]; ! name[len] = NUL; ! di = find_var_in_ht(ht, 0, name, TRUE); ! name[len] = cc; ! return di == NULL ? FAIL: OK; } /* * Check if "p[len]" is already defined, either in script "import_sid" or in ! * compilation context "cctx". * Does not check the global namespace. * Return FAIL and give an error if it defined. */ --- 319,368 ---- } /* ! * Lookup a variable (without s: prefix) in the current script. * If "vim9script" is TRUE the script must be Vim9 script. Used for "var" * without "s:". + * "cctx" is NULL at the script level. * Returns OK or FAIL. */ static int ! script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx) { ! int is_vim9_script; if (current_sctx.sc_sid <= 0) return FAIL; ! is_vim9_script = script_is_vim9(); ! if (vim9script && !is_vim9_script) return FAIL; ! if (is_vim9_script) ! { ! // Check script variables that were visible where the function was ! // defined. ! if (find_script_var(name, len, cctx) != NULL) ! return OK; ! } ! else ! { ! hashtab_T *ht = &SCRIPT_VARS(current_sctx.sc_sid); ! dictitem_T *di; ! int cc; ! ! // Check script variables that are currently visible ! cc = name[len]; ! name[len] = NUL; ! di = find_var_in_ht(ht, 0, name, TRUE); ! name[len] = cc; ! if (di != NULL) ! return OK; ! } ! ! return FAIL; } /* * Check if "p[len]" is already defined, either in script "import_sid" or in ! * compilation context "cctx". "cctx" is NULL at the script level. * Does not check the global namespace. * Return FAIL and give an error if it defined. */ *************** *** 305,314 **** ufunc_T *ufunc = NULL; p[len] = NUL; ! if (lookup_script(p, len, FALSE) == OK || (cctx != NULL && (lookup_local(p, len, cctx) != NULL ! || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) { --- 373,382 ---- ufunc_T *ufunc = NULL; 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) { *************** *** 1699,1705 **** { lvar_T *lvar; ! if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK) { emsg_namelen(_(e_str_is_used_as_argument), name, (int)len); return NULL; --- 1767,1773 ---- { lvar_T *lvar; ! if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK) { emsg_namelen(_(e_str_is_used_as_argument), name, (int)len); return NULL; *************** *** 1760,1775 **** * If not found returns -2. */ int ! get_script_item_idx(int sid, char_u *name, int check_writable) { hashtab_T *ht; dictitem_T *di; scriptitem_T *si = SCRIPT_ITEM(sid); int idx; - // First look the name up in the hashtable. if (!SCRIPT_ID_VALID(sid)) return -1; ht = &SCRIPT_VARS(sid); di = find_var_in_ht(ht, 0, name, TRUE); if (di == NULL) --- 1828,1857 ---- * If not found returns -2. */ int ! get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) { hashtab_T *ht; dictitem_T *di; scriptitem_T *si = SCRIPT_ITEM(sid); + svar_T *sv; int idx; if (!SCRIPT_ID_VALID(sid)) return -1; + if (sid == current_sctx.sc_sid) + { + sallvar_T *sav = find_script_var(name, (size_t)-1, cctx); + + if (sav == NULL) + return -2; + idx = sav->sav_var_vals_idx; + sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; + if (check_writable && sv->sv_const) + semsg(_(e_readonlyvar), name); + return idx; + } + + // First look the name up in the hashtable. ht = &SCRIPT_VARS(sid); di = find_var_in_ht(ht, 0, name, TRUE); if (di == NULL) *************** *** 1778,1785 **** // Now find the svar_T index in sn_var_vals. for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) { ! svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; ! if (sv->sv_tv == &di->di_tv) { if (check_writable && sv->sv_const) --- 1860,1866 ---- // Now find the svar_T index in sn_var_vals. for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) { ! sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; if (sv->sv_tv == &di->di_tv) { if (check_writable && sv->sv_const) *************** *** 2083,2089 **** if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return FAIL; si = SCRIPT_ITEM(current_sctx.sc_sid); ! idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE); if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9) { // variable is not in sn_var_vals: old style script. --- 2164,2170 ---- if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return FAIL; si = SCRIPT_ITEM(current_sctx.sc_sid); ! idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx); if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9) { // variable is not in sn_var_vals: old style script. *************** *** 2130,2136 **** cc = *p; *p = NUL; ! idx = find_exported(import->imp_sid, exp_name, &ufunc, &type); *p = cc; p = skipwhite(p); --- 2211,2217 ---- cc = *p; *p = NUL; ! idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, cctx); *p = cc; p = skipwhite(p); *************** *** 2257,2263 **** if (name == NULL) return FAIL; ! if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK) { if (!gen_load_outer) gen_load = TRUE; --- 2338,2344 ---- if (name == NULL) return FAIL; ! if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK) { if (!gen_load_outer) gen_load = TRUE; *************** *** 2279,2285 **** { // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. ! if (lookup_script(*arg, len, TRUE) == OK || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); --- 2360,2366 ---- { // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. ! if (script_var_exists(*arg, len, TRUE, cctx) == OK || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); *************** *** 4468,4474 **** eap->skip = cctx->ctx_skip == SKIP_YES; eap->forceit = FALSE; lambda_name = get_lambda_name(); ! ufunc = def_function(eap, lambda_name); if (ufunc == NULL) return eap->skip ? (char_u *)"" : NULL; --- 4549,4555 ---- eap->skip = cctx->ctx_skip == SKIP_YES; eap->forceit = FALSE; lambda_name = get_lambda_name(); ! ufunc = define_function(eap, lambda_name); if (ufunc == NULL) return eap->skip ? (char_u *)"" : NULL; *************** *** 4494,4505 **** --- 4575,4599 ---- // Define a local variable for the function reference. lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start, TRUE, ufunc->uf_func_type); + int block_depth = cctx->ctx_ufunc->uf_block_depth; if (lvar == NULL) return NULL; if (generate_FUNCREF(cctx, ufunc) == FAIL) return NULL; r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); + + // copy over the block scope IDs + if (block_depth > 0) + { + ufunc->uf_block_ids = ALLOC_MULT(int, block_depth); + if (ufunc->uf_block_ids != NULL) + { + mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids, + sizeof(int) * block_depth); + ufunc->uf_block_depth = block_depth; + } + } } // TODO: warning for trailing text? *************** *** 4900,4906 **** if (lvar == NULL) { CLEAR_FIELD(arg_lvar); ! if (lookup_arg(var_start, varlen, &arg_lvar.lv_idx, &arg_lvar.lv_type, &arg_lvar.lv_from_outer, cctx) == OK) { --- 4994,5000 ---- if (lvar == NULL) { CLEAR_FIELD(arg_lvar); ! if (arg_exists(var_start, varlen, &arg_lvar.lv_idx, &arg_lvar.lv_type, &arg_lvar.lv_from_outer, cctx) == OK) { *************** *** 4925,4932 **** int script_namespace = varlen > 1 && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ! ? lookup_script(var_start + 2, varlen - 2, FALSE) ! : lookup_script(var_start, varlen, TRUE)) == OK; imported_T *import = find_imported(var_start, varlen, cctx); --- 5019,5028 ---- int script_namespace = varlen > 1 && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ! ? script_var_exists(var_start + 2, varlen - 2, ! FALSE, cctx) ! : script_var_exists(var_start, varlen, ! TRUE, cctx)) == OK; imported_T *import = find_imported(var_start, varlen, cctx); *************** *** 4962,4968 **** if (SCRIPT_ID_VALID(scriptvar_sid)) { scriptvar_idx = get_script_item_idx(scriptvar_sid, ! rawname, TRUE); if (scriptvar_idx >= 0) { scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid); --- 5058,5064 ---- if (SCRIPT_ID_VALID(scriptvar_sid)) { scriptvar_idx = get_script_item_idx(scriptvar_sid, ! rawname, TRUE, cctx); if (scriptvar_idx >= 0) { scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid); *************** *** 6964,6972 **** || *ea.cmd == '@' || ((len) > 2 && ea.cmd[1] == ':') || lookup_local(ea.cmd, len, &cctx) != NULL ! || lookup_arg(ea.cmd, len, NULL, NULL, NULL, &cctx) == OK ! || lookup_script(ea.cmd, len, FALSE) == OK || find_imported(ea.cmd, len, &cctx) != NULL) { line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); --- 7060,7069 ---- || *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, ! FALSE, &cctx) == OK || find_imported(ea.cmd, len, &cctx) != NULL) { line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); *** ../vim-8.2.1845/src/proto/vim9compile.pro 2020-09-19 15:16:46.395622457 +0200 --- src/proto/vim9compile.pro 2020-10-14 21:15:29.052855837 +0200 *************** *** 1,7 **** /* vim9compile.c */ int check_defined(char_u *p, size_t len, cctx_T *cctx); int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2); ! int get_script_item_idx(int sid, char_u *name, int check_writable); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); imported_T *find_imported_in_script(char_u *name, size_t len, int sid); int vim9_comment_start(char_u *p); --- 1,7 ---- /* vim9compile.c */ int check_defined(char_u *p, size_t len, cctx_T *cctx); int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2); ! int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); imported_T *find_imported_in_script(char_u *name, size_t len, int sid); int vim9_comment_start(char_u *p); *** ../vim-8.2.1845/src/userfunc.c 2020-10-04 13:42:30.299568231 +0200 --- src/userfunc.c 2020-10-14 22:38:53.471793794 +0200 *************** *** 1134,1139 **** --- 1134,1140 ---- ga_clear_strings(&(fp->uf_lines)); VIM_CLEAR(fp->uf_arg_types); VIM_CLEAR(fp->uf_def_arg_idx); + VIM_CLEAR(fp->uf_block_ids); VIM_CLEAR(fp->uf_va_name); clear_type_list(&fp->uf_type_list); *************** *** 2658,2664 **** * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! def_function(exarg_T *eap, char_u *name_arg) { char_u *theline; char_u *line_to_free = NULL; --- 2659,2665 ---- * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! define_function(exarg_T *eap, char_u *name_arg) { char_u *theline; char_u *line_to_free = NULL; *************** *** 3477,3485 **** // error messages are for the first function line SOURCING_LNUM = sourcing_lnum_top; // parse the argument types ga_init2(&fp->uf_type_list, sizeof(type_T *), 10); - if (argtypes.ga_len > 0) { // When "varargs" is set the last name/type goes into uf_va_name --- 3478,3501 ---- // error messages are for the first function line SOURCING_LNUM = sourcing_lnum_top; + if (eap->cstack != NULL && eap->cstack->cs_idx >= 0) + { + int count = eap->cstack->cs_idx + 1; + + // The block context may be needed for script variables declared in + // a block visible now but not when the function is compiled. + fp->uf_block_ids = ALLOC_MULT(int, count); + if (fp->uf_block_ids != NULL) + { + mch_memmove(fp->uf_block_ids, eap->cstack->cs_block_id, + sizeof(int) * count); + fp->uf_block_depth = count; + } + // TODO: set flag in each block to indicate a function was defined + } + // parse the argument types ga_init2(&fp->uf_type_list, sizeof(type_T *), 10); if (argtypes.ga_len > 0) { // When "varargs" is set the last name/type goes into uf_va_name *************** *** 3608,3614 **** void ex_function(exarg_T *eap) { ! (void)def_function(eap, NULL); } /* --- 3624,3630 ---- void ex_function(exarg_T *eap) { ! (void)define_function(eap, NULL); } /* *** ../vim-8.2.1845/src/proto/userfunc.pro 2020-09-19 18:19:15.667278827 +0200 --- src/proto/userfunc.pro 2020-10-14 21:03:05.719433478 +0200 *************** *** 29,35 **** char_u *printable_func_name(ufunc_T *fp); char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial); char_u *untrans_function_name(char_u *name); ! ufunc_T *def_function(exarg_T *eap, char_u *name_arg); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); --- 29,35 ---- char_u *printable_func_name(ufunc_T *fp); char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial); char_u *untrans_function_name(char_u *name); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); *** ../vim-8.2.1845/src/ex_eval.c 2020-10-14 19:39:16.041002546 +0200 --- src/ex_eval.c 2020-10-15 12:13:34.818381060 +0200 *************** *** 918,924 **** scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len; ! cstack->cs_block_id[cstack->cs_idx] = ++si->sn_current_block_id; } } --- 918,925 ---- scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len; ! cstack->cs_block_id[cstack->cs_idx] = ++si->sn_last_block_id; ! si->sn_current_block_id = si->sn_last_block_id; } } *************** *** 938,948 **** if (sv->sv_name != NULL) // Remove a variable declared inside the block, if it still // exists, from sn_vars and move the value into sn_all_vars. ! hide_script_var(si, sv); } // TODO: is this needed? cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len; } --cstack->cs_idx; } --- 939,954 ---- if (sv->sv_name != NULL) // Remove a variable declared inside the block, if it still // exists, from sn_vars and move the value into sn_all_vars. ! hide_script_var(si, i); } // TODO: is this needed? cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len; + + if (cstack->cs_idx == 0) + si->sn_current_block_id = 0; + else + si->sn_current_block_id = cstack->cs_block_id[cstack->cs_idx - 1]; } --cstack->cs_idx; } *** ../vim-8.2.1845/src/vim9script.c 2020-10-14 19:39:16.041002546 +0200 --- src/vim9script.c 2020-10-15 12:13:07.746459477 +0200 *************** *** 193,199 **** int sid, char_u *name, ufunc_T **ufunc, ! type_T **type) { int idx = -1; svar_T *sv; --- 193,200 ---- int sid, char_u *name, ufunc_T **ufunc, ! type_T **type, ! cctx_T *cctx) { int idx = -1; svar_T *sv; *************** *** 201,207 **** // find name in "script" // TODO: also find script-local user function ! idx = get_script_item_idx(sid, name, FALSE); if (idx >= 0) { sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; --- 202,208 ---- // find name in "script" // TODO: also find script-local user function ! idx = get_script_item_idx(sid, name, FALSE, cctx); if (idx >= 0) { sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; *************** *** 248,253 **** --- 249,255 ---- /* * Handle an ":import" command and add the resulting imported_T to "gap", when * not NULL, or script "import_sid" sn_imports. + * "cctx" is NULL at the script level. * Returns a pointer to after the command or NULL in case of failure */ char_u * *************** *** 461,467 **** ufunc_T *ufunc = NULL; type_T *type; ! idx = find_exported(sid, name, &ufunc, &type); if (idx < 0 && ufunc == NULL) goto erret; --- 463,469 ---- ufunc_T *ufunc = NULL; type_T *type; ! idx = find_exported(sid, name, &ufunc, &type, cctx); if (idx < 0 && ufunc == NULL) goto erret; *************** *** 623,631 **** } } void ! hide_script_var(scriptitem_T *si, svar_T *sv) { hashtab_T *script_ht = get_script_local_ht(); hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab; hashitem_T *script_hi; --- 625,638 ---- } } + /* + * Hide a script variable when leaving a block. + * "idx" is de index in sn_var_vals. + */ void ! hide_script_var(scriptitem_T *si, int idx) { + svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; hashtab_T *script_ht = get_script_local_ht(); hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab; hashitem_T *script_hi; *************** *** 640,650 **** dictitem_T *di = HI2DI(script_hi); sallvar_T *sav = HI2SAV(all_hi); ! sav->sav_tv = di->di_tv; ! di->di_tv.v_type = VAR_UNKNOWN; ! sav->sav_flags = di->di_flags; ! sav->sav_di = NULL; ! delete_var(script_ht, script_hi); } } --- 647,665 ---- dictitem_T *di = HI2DI(script_hi); sallvar_T *sav = HI2SAV(all_hi); ! // There can be multiple entries with the same name in different ! // blocks, find the right one. ! while (sav != NULL && sav->sav_var_vals_idx != idx) ! sav = sav->sav_next; ! if (sav != NULL) ! { ! sav->sav_tv = di->di_tv; ! di->di_tv.v_type = VAR_UNKNOWN; ! sav->sav_flags = di->di_flags; ! sav->sav_di = NULL; ! delete_var(script_ht, script_hi); ! sv->sv_tv = &sav->sav_tv; ! } } } *** ../vim-8.2.1845/src/proto/vim9script.pro 2020-10-14 19:39:16.041002546 +0200 --- src/proto/vim9script.pro 2020-10-15 12:13:41.850360716 +0200 *************** *** 5,15 **** void ex_export(exarg_T *eap); void free_imports_and_script_vars(int sid); void ex_import(exarg_T *eap); ! int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type); ! void hide_script_var(scriptitem_T *si, svar_T *sv); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); --- 5,15 ---- void ex_export(exarg_T *eap); void free_imports_and_script_vars(int sid); void ex_import(exarg_T *eap); ! int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type); ! void hide_script_var(scriptitem_T *si, int idx); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); *** ../vim-8.2.1845/src/vim9execute.c 2020-10-10 14:12:58.024646147 +0200 --- src/vim9execute.c 2020-10-15 12:16:32.313868932 +0200 *************** *** 2962,2969 **** svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + iptr->isn_arg.script.script_idx; ! smsg("%4d LOADSCRIPT %s from %s", current, ! sv->sv_name, si->sn_name); } break; case ISN_LOADS: --- 2962,2971 ---- svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + iptr->isn_arg.script.script_idx; ! smsg("%4d LOADSCRIPT %s-%d from %s", current, ! sv->sv_name, ! iptr->isn_arg.script.script_idx, ! si->sn_name); } break; case ISN_LOADS: *************** *** 3054,3061 **** svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + iptr->isn_arg.script.script_idx; ! smsg("%4d STORESCRIPT %s in %s", current, ! sv->sv_name, si->sn_name); } break; case ISN_STOREOPT: --- 3056,3065 ---- svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + iptr->isn_arg.script.script_idx; ! smsg("%4d STORESCRIPT %s-%d in %s", current, ! sv->sv_name, ! iptr->isn_arg.script.script_idx, ! si->sn_name); } break; case ISN_STOREOPT: *** ../vim-8.2.1845/src/testdir/test_vim9_script.vim 2020-10-10 21:33:42.407033514 +0200 --- src/testdir/test_vim9_script.vim 2020-10-15 12:21:46.588968472 +0200 *************** *** 250,255 **** --- 250,285 ---- CheckDefFailure(['{', 'echo 1'], 'E1026:') enddef + def Test_block_local_vars() + var lines =<< trim END + vim9script + if true + var text = 'hello' + def SayHello(): string + return text + enddef + def SetText(v: string) + text = v + enddef + endif + + if true + var text = 'again' + def SayAgain(): string + return text + enddef + endif + defcompile + + assert_equal('hello', SayHello()) + assert_equal('again', SayAgain()) + + SetText('foobar') + assert_equal('foobar', SayHello()) + END + CheckScriptSuccess(lines) + enddef + func g:NoSuchFunc() echo 'none' endfunc *************** *** 1265,1279 **** assert_equal(9876, g:imported_abs) assert_equal(8888, g:imported_after) ! assert_match('\d\+_UseExported.*' .. ! 'g:imported_abs = exported.*' .. ! '0 LOADSCRIPT exported from .*Xexport_abs.vim.*' .. ! '1 STOREG g:imported_abs.*' .. ! 'exported = 8888.*' .. ! '3 STORESCRIPT exported in .*Xexport_abs.vim.*' .. ! 'g:imported_after = exported.*' .. ! '4 LOADSCRIPT exported from .*Xexport_abs.vim.*' .. ! '5 STOREG g:imported_after.*', g:import_disassembled) Undo_export_script_lines() --- 1295,1310 ---- assert_equal(9876, g:imported_abs) assert_equal(8888, g:imported_after) ! assert_match('\d\+_UseExported\_s*' .. ! 'g:imported_abs = exported\_s*' .. ! '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' .. ! '1 STOREG g:imported_abs\_s*' .. ! 'exported = 8888\_s*' .. ! '2 PUSHNR 8888\_s*' .. ! '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' .. ! 'g:imported_after = exported\_s*' .. ! '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' .. ! '5 STOREG g:imported_after', g:import_disassembled) Undo_export_script_lines() *** ../vim-8.2.1845/src/version.c 2020-10-14 19:39:16.041002546 +0200 --- src/version.c 2020-10-15 12:44:31.001534972 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1846, /**/ -- From "know your smileys": :~) A man with a tape recorder up his nose /// 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 ///