To: vim_dev@googlegroups.com Subject: Patch 8.2.0703 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0703 Problem: Vim9: closure cannot store value in outer context. Solution: Make storing value in outer context work. Make :disassemble accept a function reference. Files: src/vim9compile.c, src/vim9execute.c, src/vim9.h, src/eval.c, src/structs.h, src/testdir/test_vim9_disassemble.vim, src/testdir/test_vim9_func.vim *** ../vim-8.2.0702/src/vim9compile.c 2020-05-05 19:46:16.653307561 +0200 --- src/vim9compile.c 2020-05-06 20:24:22.374293691 +0200 *************** *** 4496,4502 **** generate_LOADV(cctx, name + 2, TRUE); break; case dest_local: ! generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); break; } } --- 4496,4506 ---- generate_LOADV(cctx, name + 2, TRUE); break; case dest_local: ! if (lvar->lv_from_outer) ! generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx, ! NULL, type); ! else ! generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); break; } } *************** *** 4713,4720 **** // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE // into ISN_STORENR ! if (instr->ga_len == instr_count + 1 ! && isn->isn_type == ISN_PUSHNR) { varnumber_T val = isn->isn_arg.number; garray_T *stack = &cctx->ctx_type_stack; --- 4717,4724 ---- // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE // into ISN_STORENR ! if (!lvar->lv_from_outer && instr->ga_len == instr_count + 1 ! && isn->isn_type == ISN_PUSHNR) { varnumber_T val = isn->isn_arg.number; garray_T *stack = &cctx->ctx_type_stack; *************** *** 4725,4730 **** --- 4729,4736 ---- if (stack->ga_len > 0) --stack->ga_len; } + else if (lvar->lv_from_outer) + generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx, NULL); else generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } *************** *** 6686,6691 **** --- 6692,6698 ---- case ISN_PUSHSPEC: case ISN_RETURN: case ISN_STORE: + case ISN_STOREOUTER: case ISN_STOREV: case ISN_STORENR: case ISN_STOREREG: *** ../vim-8.2.0702/src/vim9execute.c 2020-05-05 22:08:20.602179819 +0200 --- src/vim9execute.c 2020-05-06 20:27:15.369647265 +0200 *************** *** 1070,1075 **** --- 1070,1083 ---- *tv = *STACK_TV_BOT(0); break; + // store variable or argument in outer scope + case ISN_STOREOUTER: + --ectx.ec_stack.ga_len; + tv = STACK_OUT_TV_VAR(iptr->isn_arg.number); + clear_tv(tv); + *tv = *STACK_TV_BOT(0); + break; + // store s: variable in old script case ISN_STORES: { *************** *** 2133,2139 **** int is_global = FALSE; fname = trans_function_name(&arg, &is_global, FALSE, ! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL); if (fname == NULL) { semsg(_(e_invarg2), eap->arg); --- 2141,2147 ---- int is_global = FALSE; fname = trans_function_name(&arg, &is_global, FALSE, ! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL); if (fname == NULL) { semsg(_(e_invarg2), eap->arg); *************** *** 2275,2286 **** break; case ISN_STORE: if (iptr->isn_arg.number < 0) ! smsg("%4d STORE arg[%lld]", current, (long long)(iptr->isn_arg.number + STACK_FRAME_SIZE)); else ! smsg("%4d STORE $%lld", current, (long long)(iptr->isn_arg.number)); break; case ISN_STOREV: smsg("%4d STOREV v:%s", current, --- 2283,2299 ---- break; case ISN_STORE: + case ISN_STOREOUTER: + { + char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER"; + if (iptr->isn_arg.number < 0) ! smsg("%4d STORE%s arg[%lld]", current, add, (long long)(iptr->isn_arg.number + STACK_FRAME_SIZE)); else ! smsg("%4d STORE%s $%lld", current, add, (long long)(iptr->isn_arg.number)); + } break; case ISN_STOREV: smsg("%4d STOREV v:%s", current, *** ../vim-8.2.0702/src/vim9.h 2020-05-03 15:38:12.983700666 +0200 --- src/vim9.h 2020-05-06 20:23:39.702453780 +0200 *************** *** 40,47 **** ISN_STOREW, // pop into window-local variable isn_arg.string ISN_STORET, // pop into tab-local variable isn_arg.string ISN_STORES, // pop into script variable isn_arg.loadstore ISN_STORESCRIPT, // pop into script variable isn_arg.script ! ISN_STOREOPT, // pop into option isn_arg.string ISN_STOREENV, // pop into environment variable isn_arg.string ISN_STOREREG, // pop into register isn_arg.number // ISN_STOREOTHER, // pop into other script variable isn_arg.other. --- 40,48 ---- ISN_STOREW, // pop into window-local variable isn_arg.string ISN_STORET, // pop into tab-local variable isn_arg.string ISN_STORES, // pop into script variable isn_arg.loadstore + ISN_STOREOUTER, // pop variable into outer scope isn_arg.number ISN_STORESCRIPT, // pop into script variable isn_arg.script ! ISN_STOREOPT, // pop into option isn_arg.string ISN_STOREENV, // pop into environment variable isn_arg.string ISN_STOREREG, // pop into register isn_arg.number // ISN_STOREOTHER, // pop into other script variable isn_arg.other. *** ../vim-8.2.0702/src/eval.c 2020-05-03 15:38:12.987700652 +0200 --- src/eval.c 2020-05-06 20:57:38.679234649 +0200 *************** *** 4337,4345 **** partial_T *pt = tv->vval.v_partial; int i; ! // A partial does not have a copyID, because it cannot contain itself. ! if (pt != NULL) { abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) --- 4337,4347 ---- partial_T *pt = tv->vval.v_partial; int i; ! if (pt != NULL && pt->pt_copyID != copyID) { + // Didn't see this partial yet. + pt->pt_copyID = copyID; + abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); if (pt->pt_dict != NULL) *** ../vim-8.2.0702/src/structs.h 2020-05-03 15:38:12.983700666 +0200 --- src/structs.h 2020-05-06 20:55:42.087616498 +0200 *************** *** 1812,1817 **** --- 1812,1818 ---- typval_T *pt_argv; // arguments in allocated array dict_T *pt_dict; // dict for "self" + int pt_copyID; // funcstack may contain pointer to partial }; typedef struct AutoPatCmd_S AutoPatCmd; *** ../vim-8.2.0702/src/testdir/test_vim9_disassemble.vim 2020-04-25 20:02:36.001096124 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-05-06 20:42:43.290215072 +0200 *************** *** 291,296 **** --- 291,332 ---- res) enddef + def s:CreateRefs() + let local = 'a' + def Append(arg: string) + local ..= arg + enddef + g:Append = Append + def Get(): string + return local + enddef + g:Get = Get + enddef + + def Test_disassemble_closure() + CreateRefs() + let res = execute('disass g:Append') + assert_match('\d.*' .. + 'local ..= arg.*' .. + '\d LOADOUTER $0.*' .. + '\d LOAD arg\[-1\].*' .. + '\d CONCAT.*' .. + '\d STOREOUTER $0.*' .. + '\d PUSHNR 0.*' .. + '\d RETURN.*', + res) + + res = execute('disass g:Get') + assert_match('\d.*' .. + 'return local.*' .. + '\d LOADOUTER $0.*' .. + '\d RETURN.*', + res) + + unlet g:Append + unlet g:Get + enddef + def EchoArg(arg: string): string return arg *** ../vim-8.2.0702/src/testdir/test_vim9_func.vim 2020-05-05 21:25:19.141316752 +0200 --- src/testdir/test_vim9_func.vim 2020-05-06 20:33:55.068163772 +0200 *************** *** 738,743 **** --- 738,769 ---- unlet g:UseVararg enddef + def MakeGetAndAppendRefs() + let local = 'a' + + def Append(arg: string) + local ..= arg + enddef + g:Append = Append + + def Get(): string + return local + enddef + g:Get = Get + enddef + + def Test_closure_append_get() + MakeGetAndAppendRefs() + assert_equal('a', g:Get()) + g:Append('-b') + assert_equal('a-b', g:Get()) + g:Append('-c') + assert_equal('a-b-c', g:Get()) + + unlet g:Append + unlet g:Get + enddef + def Test_nested_closure() let local = 'text' def Closure(arg: string): string *** ../vim-8.2.0702/src/version.c 2020-05-06 19:37:48.524006767 +0200 --- src/version.c 2020-05-06 20:55:58.951561789 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 703, /**/ -- Q: What is a patch 22? A: A patch you need to include to make it possible to include patches. /// 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 ///