To: vim_dev@googlegroups.com Subject: Patch 9.0.0460 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0460 (after 9.0.0459) Problem: Loop variable can't be found. Solution: Adjust block_id of the loop variable each round. Files: src/ex_eval.c, src/vim9compile.c, src/proto/vim9compile.pro, src/eval.c, src/structs.h, src/evalvars.c, src/vim.h, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_script.vim, src/testdir/dumps/Test_vim9_closure_fails.dump *** ../vim-9.0.0459/src/ex_eval.c 2022-09-13 21:10:41.092155585 +0100 --- src/ex_eval.c 2022-09-13 23:40:21.703223090 +0100 *************** *** 1208,1213 **** --- 1208,1214 ---- int skip; int result; cstack_T *cstack = eap->cstack; + int prev_cs_flags = 0; if (cstack->cs_idx == CSTACK_LEN - 1) eap->errmsg = _(e_while_for_nesting_too_deep); *************** *** 1261,1266 **** --- 1262,1268 ---- si->sn_current_block_id = si->sn_last_block_id; } } + prev_cs_flags = cstack->cs_flags[cstack->cs_idx]; cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; *************** *** 1279,1285 **** } else { ! void *fi; evalarg_T evalarg; /* --- 1281,1287 ---- } else { ! forinfo_T *fi; evalarg_T evalarg; /* *************** *** 1313,1321 **** --- 1315,1332 ---- result = next_for_item(fi, eap->arg); else result = FALSE; + if (fi != NULL) + // OR all the cs_flags together, if a function was defined in + // any round then the loop variable may have been used. + fi->fi_cs_flags |= prev_cs_flags; if (!result) { + // If a function was defined in any round then set the + // CSF_FUNC_DEF flag now, so that it's seen by leave_block(). + if (fi != NULL && (fi->fi_cs_flags & CSF_FUNC_DEF)) + cstack->cs_flags[cstack->cs_idx] |= CSF_FUNC_DEF; + free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } *** ../vim-9.0.0459/src/vim9compile.c 2022-09-03 21:35:50.184158219 +0100 --- src/vim9compile.c 2022-09-13 23:57:34.973541036 +0100 *************** *** 183,188 **** --- 183,191 ---- if (cctx == NULL) { + if (cstack == NULL) + return NULL; + // Not in a function scope, find variable with block ID equal to or // smaller than the current block id. Use "cstack" to go up the block // scopes. *************** *** 220,225 **** --- 223,245 ---- } /* + * If "name" can be found in the current script set it's "block_id". + */ + void + update_script_var_block_id(char_u *name, int block_id) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + hashitem_T *hi; + sallvar_T *sav; + + hi = hash_find(&si->sn_all_vars.dv_hashtab, name); + if (HASHITEM_EMPTY(hi)) + return; + sav = HI2SAV(hi); + sav->sav_block_id = block_id; + } + + /* * Return TRUE if the script context is Vim9 script. */ int *** ../vim-9.0.0459/src/proto/vim9compile.pro 2022-06-27 23:15:29.000000000 +0100 --- src/proto/vim9compile.pro 2022-09-13 22:40:31.343177877 +0100 *************** *** 1,6 **** --- 1,7 ---- /* vim9compile.c */ int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx); int arg_exists(char_u *name, size_t len, int *idxp, type_T **type, int *gen_load_outer, cctx_T *cctx); + void update_script_var_block_id(char_u *name, int block_id); int script_is_vim9(void); int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack); int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg); *** ../vim-9.0.0459/src/eval.c 2022-09-11 15:14:00.551020049 +0100 --- src/eval.c 2022-09-13 23:42:22.055097447 +0100 *************** *** 29,50 **** */ static int current_copyID = 0; - /* - * Info used by a ":for" loop. - */ - typedef struct - { - int fi_semicolon; // TRUE if ending in '; var]' - int fi_varcount; // nr of variables in the list - int fi_break_count; // nr of line breaks encountered - listwatch_T fi_lw; // keep an eye on the item used. - list_T *fi_list; // list being used - int fi_bi; // index of blob - blob_T *fi_blob; // blob being used - char_u *fi_string; // copy of string being used - int fi_byte_idx; // byte index in fi_string - } forinfo_T; - static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg); --- 29,34 ---- *************** *** 1914,1920 **** ? (ASSIGN_FINAL // first round: error if variable exists | (fi->fi_bi == 0 ? 0 : ASSIGN_DECL) ! | ASSIGN_NO_MEMBER_TYPE) : 0); listitem_T *item; int skip_assign = in_vim9script() && arg[0] == '_' --- 1898,1905 ---- ? (ASSIGN_FINAL // first round: error if variable exists | (fi->fi_bi == 0 ? 0 : ASSIGN_DECL) ! | ASSIGN_NO_MEMBER_TYPE ! | ASSIGN_UPDATE_BLOCK_ID) : 0); listitem_T *item; int skip_assign = in_vim9script() && arg[0] == '_' *** ../vim-9.0.0459/src/structs.h 2022-09-11 16:59:48.938110035 +0100 --- src/structs.h 2022-09-13 23:43:43.366996089 +0100 *************** *** 1626,1631 **** --- 1626,1648 ---- typedef struct svar_S svar_T; #if defined(FEAT_EVAL) || defined(PROTO) + /* + * Info used by a ":for" loop. + */ + typedef struct + { + int fi_semicolon; // TRUE if ending in '; var]' + int fi_varcount; // nr of variables in the list + int fi_break_count; // nr of line breaks encountered + listwatch_T fi_lw; // keep an eye on the item used. + list_T *fi_list; // list being used + int fi_bi; // index of blob + blob_T *fi_blob; // blob being used + char_u *fi_string; // copy of string being used + int fi_byte_idx; // byte index in fi_string + int fi_cs_flags; // cs_flags or'ed together + } forinfo_T; + typedef struct funccall_S funccall_T; // values used for "uf_def_status" *** ../vim-9.0.0459/src/evalvars.c 2022-09-11 15:14:00.551020049 +0100 --- src/evalvars.c 2022-09-13 22:52:18.501260716 +0100 *************** *** 3851,3856 **** --- 3851,3864 ---- } clear_tv(&di->di_tv); + + if ((flags & ASSIGN_UPDATE_BLOCK_ID) + && SCRIPT_ID_VALID(current_sctx.sc_sid)) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + + update_script_var_block_id(name, si->sn_current_block_id); + } } else { *** ../vim-9.0.0459/src/vim.h 2022-08-31 17:48:05.711547579 +0100 --- src/vim.h 2022-09-13 22:50:33.945541935 +0100 *************** *** 2258,2263 **** --- 2258,2264 ---- #define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type #define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable #define ASSIGN_INIT 0x80 // not assigning a value, just a declaration + #define ASSIGN_UPDATE_BLOCK_ID 0x100 // update sav_block_id #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff *** ../vim-9.0.0459/src/testdir/test_vim9_func.vim 2022-09-11 12:01:01.049365323 +0100 --- src/testdir/test_vim9_func.vim 2022-09-14 00:26:59.357539178 +0100 *************** *** 2930,2937 **** def Run_Test_closure_in_for_loop_fails() var lines =<< trim END vim9script for n in [0] ! timer_start(10, (_) => { echo n }) endfor --- 2930,2939 ---- def Run_Test_closure_in_for_loop_fails() var lines =<< trim END vim9script + redraw for n in [0] ! # time should be enough for startup to finish ! timer_start(200, (_) => { echo n }) endfor *************** *** 2940,2946 **** # Check that an error shows var buf = g:RunVimInTerminal('-S XTest_closure_fails', {rows: 6, wait_for_ruler: 0}) ! g:VerifyScreenDump(buf, 'Test_vim9_closure_fails', {}) # clean up g:StopVimInTerminal(buf) --- 2942,2948 ---- # Check that an error shows var buf = g:RunVimInTerminal('-S XTest_closure_fails', {rows: 6, wait_for_ruler: 0}) ! g:VerifyScreenDump(buf, 'Test_vim9_closure_fails', {wait: 3000}) # clean up g:StopVimInTerminal(buf) *** ../vim-9.0.0459/src/testdir/test_vim9_script.vim 2022-09-13 21:10:41.092155585 +0100 --- src/testdir/test_vim9_script.vim 2022-09-13 23:51:34.434235974 +0100 *************** *** 2259,2267 **** --- 2259,2281 ---- enddef def Test_for_loop_with_closure() + # using the loop variable in a closure results in the last used value var lines =<< trim END var flist: list for i in range(5) + flist[i] = () => i + endfor + for i in range(5) + assert_equal(4, flist[i]()) + endfor + END + v9.CheckDefAndScriptSuccess(lines) + + # using a local variable set to the loop variable in a closure results in the + # value at that moment + lines =<< trim END + var flist: list + for i in range(5) var inloop = i flist[i] = () => inloop endfor *** ../vim-9.0.0459/src/testdir/dumps/Test_vim9_closure_fails.dump 2022-09-09 21:35:17.835126936 +0100 --- src/testdir/dumps/Test_vim9_closure_fails.dump 2022-09-14 00:02:05.892988325 +0100 *************** *** 1,6 **** ! |~+0#4040ff13#ffffff0| @73 |~| @73 ! |E+0#ffffff16#e000002|r@1|o|r| |d|e|t|e|c|t|e|d| |w|h|i|l|e| |p|r|o|c|e|s@1|i|n|g| |f|u|n|c|t|i|o|n| |<|l|a|m|b|d|a|>|1|:| +0#0000000#ffffff0@23 ! |l+0#af5f00255&|i|n|e| @3|1|:| +0#0000000&@64 ! |E+0#ffffff16#e000002|1|3|0|2|:| |S|c|r|i|p|t| |v|a|r|i|a|b|l|e| |w|a|s| |d|e|l|e|t|e|d| +0#0000000#ffffff0@40 ! |P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 --- 1,6 ---- ! > +0&#ffffff0@74 ! |~+0#4040ff13&| @73 |~| @73 ! |~| @73 ! |~| @73 ! |0+0#0000000&| @55|0|,|0|-|1| @8|A|l@1| *** ../vim-9.0.0459/src/version.c 2022-09-13 21:10:41.092155585 +0100 --- src/version.c 2022-09-14 00:28:57.325426937 +0100 *************** *** 705,706 **** --- 705,708 ---- { /* Add new patch number below this line */ + /**/ + 460, /**/ -- Time is money. Especially if you make clocks. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///