]> granicus.if.org Git - vim/commitdiff
patch 8.2.0703: Vim9: closure cannot store value in outer context v8.2.0703
authorBram Moolenaar <Bram@vim.org>
Wed, 6 May 2020 19:06:30 +0000 (21:06 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 6 May 2020 19:06:30 +0000 (21:06 +0200)
Problem:    Vim9: closure cannot store value in outer context.
Solution:   Make storing value in outer context work.  Make :disassemble
            accept a function reference.

src/eval.c
src/structs.h
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_func.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 72cb0897affcc0fc79b163a795da86e9ea3a8370..b7003a882cc2ff40ac8d60fccce74ee04ed80a6a 100644 (file)
@@ -4337,9 +4337,11 @@ set_ref_in_item(
        partial_T       *pt = tv->vval.v_partial;
        int             i;
 
-       // A partial does not have a copyID, because it cannot contain itself.
-       if (pt != NULL)
+       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)
index 7fd325a825879dd46e62b1f950d185fd57b9b71d..69233a967336200486ec7c7304a72d64c2504150 100644 (file)
@@ -1812,6 +1812,7 @@ struct partial_S
     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;
index 4e2caf09f15f3b68d79d4d0d4888937a577da390..11703416d12de43dd0e0fa5a1116bfaeb1940f3b 100644 (file)
@@ -291,6 +291,42 @@ def Test_disassemble_call()
         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('<lambda>\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('<lambda>\d.*' ..
+        'return local.*' ..
+        '\d LOADOUTER $0.*' ..
+        '\d RETURN.*',
+        res)
+
+  unlet g:Append
+  unlet g:Get
+enddef
+
 
 def EchoArg(arg: string): string
   return arg
index 49dc2f2f217538f7be4ad05cec6d4af6263f22e4..83cef67c27d3b307348b424ad47f99144141e38e 100644 (file)
@@ -738,6 +738,32 @@ def Test_closure_using_argument()
   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
index 523791ca0fdf03c56387f62d43625f7c3763c93b..b507c89b28d75cbb3160e6165d6fb5963d4a4d07 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    703,
 /**/
     702,
 /**/
index aac776af1cd10ed83f6aa5711a9e0cca7c20bd9c..709716ee3bbb8d160cf509b190a66bf309a89bbe 100644 (file)
@@ -40,8 +40,9 @@ typedef enum {
     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_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.
index 42fc0749add3fd9295ec40f34f8442e1eb243fc3..5e29cc015645487ebd1b9b94ce905992e51a9ca6 100644 (file)
@@ -4496,7 +4496,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                    generate_LOADV(cctx, name + 2, TRUE);
                    break;
                case dest_local:
-                   generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
+                   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,8 +4717,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
 
                // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE
                // into ISN_STORENR
-               if (instr->ga_len == instr_count + 1
-                                               && isn->isn_type == ISN_PUSHNR)
+               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,6 +4729,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                    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,6 +6692,7 @@ delete_instr(isn_T *isn)
        case ISN_PUSHSPEC:
        case ISN_RETURN:
        case ISN_STORE:
+       case ISN_STOREOUTER:
        case ISN_STOREV:
        case ISN_STORENR:
        case ISN_STOREREG:
index 5915ea1dceaeaca94ac7a5ddcecc29bc8c696d4b..9651f5e9b2ea5c9e4594a3075f6d5dfed1088e6b 100644 (file)
@@ -1070,6 +1070,14 @@ call_def_function(
                *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,7 +2141,7 @@ ex_disassemble(exarg_T *eap)
     int                is_global = FALSE;
 
     fname = trans_function_name(&arg, &is_global, FALSE,
-            TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
+                           TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL);
     if (fname == NULL)
     {
        semsg(_(e_invarg2), eap->arg);
@@ -2275,12 +2283,17 @@ ex_disassemble(exarg_T *eap)
                break;
 
            case ISN_STORE:
+           case ISN_STOREOUTER:
+               {
+                   char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER";
+
                if (iptr->isn_arg.number < 0)
-                   smsg("%4d STORE arg[%lld]", current,
+                   smsg("%4d STORE%s arg[%lld]", current, add,
                         (long long)(iptr->isn_arg.number + STACK_FRAME_SIZE));
                else
-                   smsg("%4d STORE $%lld", current,
+                   smsg("%4d STORE%s $%lld", current, add,
                                            (long long)(iptr->isn_arg.number));
+               }
                break;
            case ISN_STOREV:
                smsg("%4d STOREV v:%s", current,