To: vim_dev@googlegroups.com Subject: Patch 8.2.1824 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1824 Problem: Vim9: variables at the script level escape their scope. Solution: When leaving a scope remove variables declared in it. Files: src/structs.h, src/ex_eval.c, src/evalvars.c, src/proto/evalvars.pro, src/testdir/test_vim9_script.vim *** ../vim-8.2.1823/src/structs.h 2020-10-10 14:12:58.024646147 +0200 --- src/structs.h 2020-10-10 17:27:40.236527958 +0200 *************** *** 889,894 **** --- 889,896 ---- } cs_pend; void *cs_forinfo[CSTACK_LEN]; // info used by ":for" int cs_line[CSTACK_LEN]; // line nr of ":while"/":for" line + int cs_script_var_len[CSTACK_LEN]; // value of sn_var_vals.ga_len + // when entering the block int cs_idx; // current entry, or -1 if none int cs_looplevel; // nr of nested ":while"s and ":for"s int cs_trylevel; // nr of nested ":try"s *** ../vim-8.2.1823/src/ex_eval.c 2020-09-10 19:25:01.612194701 +0200 --- src/ex_eval.c 2020-10-10 18:27:24.558010168 +0200 *************** *** 906,911 **** --- 906,953 ---- } /* + * Start a new scope/block. Caller should have checked that cs_idx is not + * exceeding CSTACK_LEN. + */ + static void + enter_block(cstack_T *cstack) + { + ++cstack->cs_idx; + if (in_vim9script()) + cstack->cs_script_var_len[cstack->cs_idx] = + SCRIPT_ITEM(current_sctx.sc_sid)->sn_var_vals.ga_len; + } + + static void + leave_block(cstack_T *cstack) + { + int i; + + if (in_vim9script()) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + + for (i = cstack->cs_script_var_len[cstack->cs_idx]; + i < si->sn_var_vals.ga_len; ++i) + { + svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i; + hashtab_T *ht = get_script_local_ht(); + hashitem_T *hi; + + if (ht != NULL) + { + // Remove a variable declared inside the block, if it still + // exists. + hi = hash_find(ht, sv->sv_name); + if (!HASHITEM_EMPTY(hi)) + delete_var(ht, hi); + } + } + } + --cstack->cs_idx; + } + + /* * ":if". */ void *************** *** 920,931 **** eap->errmsg = _("E579: :if nesting too deep"); else { ! ++cstack->cs_idx; cstack->cs_flags[cstack->cs_idx] = 0; /* ! * Don't do something after an error, interrupt, or throw, or when there ! * is a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); --- 962,973 ---- eap->errmsg = _("E579: :if nesting too deep"); else { ! enter_block(cstack); cstack->cs_flags[cstack->cs_idx] = 0; /* ! * Don't do something after an error, interrupt, or throw, or when ! * there is a surrounding conditional and it was not active. */ skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)); *************** *** 949,957 **** void ex_endif(exarg_T *eap) { did_endif = TRUE; ! if (eap->cstack->cs_idx < 0 ! || (eap->cstack->cs_flags[eap->cstack->cs_idx] & (CSF_WHILE | CSF_FOR | CSF_TRY))) eap->errmsg = _(e_endif_without_if); else --- 991,1001 ---- void ex_endif(exarg_T *eap) { + cstack_T *cstack = eap->cstack; + did_endif = TRUE; ! if (cstack->cs_idx < 0 ! || (cstack->cs_flags[cstack->cs_idx] & (CSF_WHILE | CSF_FOR | CSF_TRY))) eap->errmsg = _(e_endif_without_if); else *************** *** 965,975 **** * Doing this here prevents an exception for a parsing error being * discarded by throwing the interrupt exception later on. */ ! if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE) && dbg_check_skipped(eap)) ! (void)do_intthrow(eap->cstack); ! --eap->cstack->cs_idx; } } --- 1009,1019 ---- * Doing this here prevents an exception for a parsing error being * discarded by throwing the interrupt exception later on. */ ! if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE) && dbg_check_skipped(eap)) ! (void)do_intthrow(cstack); ! leave_block(cstack); } } *************** *** 1086,1092 **** */ if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) { ! ++cstack->cs_idx; ++cstack->cs_looplevel; cstack->cs_line[cstack->cs_idx] = -1; } --- 1130,1136 ---- */ if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) { ! enter_block(cstack); ++cstack->cs_looplevel; cstack->cs_line[cstack->cs_idx] = -1; } *************** *** 1450,1456 **** eap->errmsg = _("E601: :try nesting too deep"); else { ! ++cstack->cs_idx; ++cstack->cs_trylevel; cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; --- 1494,1500 ---- eap->errmsg = _("E601: :try nesting too deep"); else { ! enter_block(cstack); ++cstack->cs_trylevel; cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; *************** *** 1923,1929 **** */ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); ! --cstack->cs_idx; --cstack->cs_trylevel; if (!skip) --- 1967,1973 ---- */ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); ! leave_block(cstack); --cstack->cs_trylevel; if (!skip) *************** *** 2303,2309 **** --*cond_level; if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) free_for_info(cstack->cs_forinfo[cstack->cs_idx]); ! --cstack->cs_idx; } } --- 2347,2353 ---- --*cond_level; if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) free_for_info(cstack->cs_forinfo[cstack->cs_idx]); ! leave_block(cstack); } } *** ../vim-8.2.1823/src/evalvars.c 2020-10-08 21:30:35.969526619 +0200 --- src/evalvars.c 2020-10-10 18:27:40.229968758 +0200 *************** *** 174,180 **** static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op); static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); - static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); --- 174,179 ---- *************** *** 2890,2896 **** * Delete a variable from hashtab "ht" at item "hi". * Clear the variable value and free the dictitem. */ ! static void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = HI2DI(hi); --- 2889,2895 ---- * Delete a variable from hashtab "ht" at item "hi". * Clear the variable value and free the dictitem. */ ! void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = HI2DI(hi); *** ../vim-8.2.1823/src/proto/evalvars.pro 2020-10-08 21:16:38.643643838 +0200 --- src/proto/evalvars.pro 2020-10-10 18:27:29.069998249 +0200 *************** *** 67,72 **** --- 67,73 ---- void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); void vars_clear_ext(hashtab_T *ht, int free_val); + void delete_var(hashtab_T *ht, hashitem_T *hi); void set_var(char_u *name, typval_T *tv, int copy); void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags); int var_check_ro(int flags, char_u *name, int use_gettext); *** ../vim-8.2.1823/src/testdir/test_vim9_script.vim 2020-10-08 21:16:38.643643838 +0200 --- src/testdir/test_vim9_script.vim 2020-10-10 18:39:33.084055199 +0200 *************** *** 2685,2690 **** --- 2685,2740 ---- delete('Xdidcmd') enddef + def Test_script_var_scope() + var lines =<< trim END + vim9script + if true + if true + var one = 'one' + echo one + endif + echo one + endif + END + CheckScriptFailure(lines, 'E121:', 7) + + lines =<< trim END + vim9script + if true + if false + var one = 'one' + echo one + else + var one = 'one' + echo one + endif + echo one + endif + END + CheckScriptFailure(lines, 'E121:', 10) + + lines =<< trim END + vim9script + while true + var one = 'one' + echo one + break + endwhile + echo one + END + CheckScriptFailure(lines, 'E121:', 7) + + lines =<< trim END + vim9script + for i in range(1) + var one = 'one' + echo one + endfor + echo one + END + CheckScriptFailure(lines, 'E121:', 6) + enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new *** ../vim-8.2.1823/src/version.c 2020-10-10 16:45:20.711469191 +0200 --- src/version.c 2020-10-10 18:42:26.595601471 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1824, /**/ -- Be thankful to be in a traffic jam, because it means you own a car. /// 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 ///