Patch 9.0.0370
Problem:    Cleaning up afterwards can make a function messy.
Solution:   Add the :defer command.
Files:      runtime/doc/eval.txt, src/ex_cmds.h, src/ex_cmdidxs.h,
            src/userfunc.c, src/proto/userfunc.pro, src/structs.h,
            src/vim9compile.c, src/vim9cmds.c, src/proto/vim9cmds.pro,
            src/vim9.h, src/vim9instr.c, src/proto/vim9instr.pro,
            src/vim9expr.c, src/proto/vim9expr.pro, src/vim9execute.c,
            src/testdir/test_user_func.vim, src/testdir/test_vim9_func.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-9.0.0369/runtime/doc/eval.txt        2022-08-29 18:16:11.574636829 
+0100
--- runtime/doc/eval.txt        2022-09-03 17:36:27.213054907 +0100
***************
*** 2978,2983 ****
--- 2979,3040 ----
        let y = GetList()->Filter()
  
  
+ CLEANING UP IN A FUNCTION ~
+                                                       *:defer*
+ :defer {func}({args}) Call {func} when the current function is done.
+                       {args} are evaluated here.
+ 
+ Quite often a command in a function has a global effect, which must be undone
+ when the function finishes.  Handling this in all kinds of situations can be a
+ hassle.  Especially when an unexpected error is encountered.  This can be done
+ with `try` / `finally` blocks, but this gets complicated when there is more
+ than one.
+ 
+ A much simpler solution is using `defer`.  It schedules a function call when
+ the function is returning, no matter if there is an error.  Example: >
+       func Filter(text)
+         call writefile(a:text, 'Tempfile')
+         call system('filter < Tempfile > Outfile')
+         call Handle('Outfile')
+         call delete('Tempfile')
+         call delete('Outfile')
+       endfunc
+ 
+ Here 'Tempfile' and 'Outfile' will not be deleted if something causes the
+ function to abort.  `:defer` can be used to avoid that: >
+       func Filter(text)
+         call writefile(a:text, 'Tempfile')
+         defer delete('Tempfile')
+         defer delete('Outfile')
+         call system('filter < Tempfile > Outfile')
+         call Handle('Outfile')
+       endfunc
+ 
+ Note that deleting "Outfile" is scheduled before calling system(), since it
+ can be created even when `system()` fails.
+ 
+ The defered functions are called in reverse order, the last one added is
+ executed first.  A useless example: >
+       func Useless()
+         for s in range(3)
+           defer execute('echomsg "number ' .. s .. '"')
+         endfor
+       endfunc
+ 
+ Now `:messages` shows:
+       number 2
+       number 1
+       number 0
+ 
+ Any return value of the deferred function is discarded.  The function cannot
+ be followed by anything, such as "->func" or ".member".  Currently `:defer
+ GetArg()->TheFunc()` does not work, it may work in a later version.
+ 
+ Errors are reported but do not cause aborting execution of deferred functions.
+ 
+ No range is accepted.
+ 
+ 
  AUTOMATICALLY LOADING FUNCTIONS ~
                                                        *autoload-functions*
  When using many or large functions, it's possible to automatically define them
*** ../vim-9.0.0369/src/ex_cmds.h       2022-08-31 17:48:05.711547579 +0100
--- src/ex_cmds.h       2022-09-03 14:45:42.389458416 +0100
***************
*** 467,472 ****
--- 467,475 ----
  EXCMD(CMD_defcompile, "defcompile",   ex_defcompile,
        EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA,
        ADDR_NONE),
+ EXCMD(CMD_defer,      "defer",        ex_call,
+       
EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
+       ADDR_NONE),
  EXCMD(CMD_delcommand, "delcommand",   ex_delcommand,
        EX_NEEDARG|EX_WORD1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
*** ../vim-9.0.0369/src/ex_cmdidxs.h    2022-08-31 17:48:05.711547579 +0100
--- src/ex_cmdidxs.h    2022-09-03 14:34:48.226709187 +0100
***************
*** 9,36 ****
    /* b */ 21,
    /* c */ 45,
    /* d */ 112,
!   /* e */ 137,
!   /* f */ 166,
!   /* g */ 183,
!   /* h */ 189,
!   /* i */ 199,
!   /* j */ 219,
!   /* k */ 221,
!   /* l */ 226,
!   /* m */ 289,
!   /* n */ 307,
!   /* o */ 327,
!   /* p */ 339,
!   /* q */ 378,
!   /* r */ 381,
!   /* s */ 401,
!   /* t */ 471,
!   /* u */ 517,
!   /* v */ 528,
!   /* w */ 549,
!   /* x */ 563,
!   /* y */ 573,
!   /* z */ 574
  };
  
  /*
--- 9,36 ----
    /* b */ 21,
    /* c */ 45,
    /* d */ 112,
!   /* e */ 138,
!   /* f */ 167,
!   /* g */ 184,
!   /* h */ 190,
!   /* i */ 200,
!   /* j */ 220,
!   /* k */ 222,
!   /* l */ 227,
!   /* m */ 290,
!   /* n */ 308,
!   /* o */ 328,
!   /* p */ 340,
!   /* q */ 379,
!   /* r */ 382,
!   /* s */ 402,
!   /* t */ 472,
!   /* u */ 518,
!   /* v */ 529,
!   /* w */ 550,
!   /* x */ 564,
!   /* y */ 574,
!   /* z */ 575
  };
  
  /*
***************
*** 44,50 ****
    /* a */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  6,  7,  0,  0,  
0,  8, 17,  0, 18,  0,  0,  0,  0,  0 },
    /* b */ {  2,  0,  0,  5,  6,  8,  0,  0,  0,  0,  0,  9, 10, 11, 12, 13,  
0, 14,  0,  0,  0,  0, 23,  0,  0,  0 },
    /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 38, 41, 47, 57, 
59, 60, 61,  0, 63,  0, 66,  0,  0,  0 },
!   /* d */ {  0,  0,  0,  0,  0,  0,  0,  0,  8, 18,  0, 19,  0,  0, 20,  0,  
0, 22, 23,  0,  0,  0,  0,  0,  0,  0 },
    /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  9, 11, 12,  0,  0,  
0,  0,  0,  0,  0, 23,  0, 24,  0,  0 },
    /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  
0,  0,  0,  0, 16,  0,  0,  0,  0,  0 },
    /* g */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  
0,  2,  0,  0,  4,  5,  0,  0,  0,  0 },
--- 44,50 ----
    /* a */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  6,  7,  0,  0,  
0,  8, 17,  0, 18,  0,  0,  0,  0,  0 },
    /* b */ {  2,  0,  0,  5,  6,  8,  0,  0,  0,  0,  0,  9, 10, 11, 12, 13,  
0, 14,  0,  0,  0,  0, 23,  0,  0,  0 },
    /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 38, 41, 47, 57, 
59, 60, 61,  0, 63,  0, 66,  0,  0,  0 },
!   /* d */ {  0,  0,  0,  0,  0,  0,  0,  0,  9, 19,  0, 20,  0,  0, 21,  0,  
0, 23, 24,  0,  0,  0,  0,  0,  0,  0 },
    /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  9, 11, 12,  0,  0,  
0,  0,  0,  0,  0, 23,  0, 24,  0,  0 },
    /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  
0,  0,  0,  0, 16,  0,  0,  0,  0,  0 },
    /* g */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  
0,  2,  0,  0,  4,  5,  0,  0,  0,  0 },
***************
*** 69,72 ****
    /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
  };
  
! static const int command_count = 591;
--- 69,72 ----
    /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
  };
  
! static const int command_count = 592;
*** ../vim-9.0.0369/src/userfunc.c      2022-08-28 18:52:06.667888932 +0100
--- src/userfunc.c      2022-09-03 16:51:07.136075989 +0100
***************
*** 1728,1771 ****
  }
  
  /*
!  * Allocate a variable for the result of a function.
!  * Return OK or FAIL.
   */
!     int
! get_func_tv(
!     char_u    *name,          // name of the function
!     int               len,            // length of "name" or -1 to use 
strlen()
!     typval_T  *rettv,
!     char_u    **arg,          // argument, pointing to the '('
!     evalarg_T *evalarg,       // for line continuation
!     funcexe_T *funcexe)       // various values
  {
!     char_u    *argp;
      int               ret = OK;
-     typval_T  argvars[MAX_FUNC_ARGS + 1];     // vars for arguments
-     int               argcount = 0;           // number of arguments found
      int               vim9script = in_vim9script();
      int               evaluate = evalarg == NULL
                               ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
  
!     /*
!      * Get the arguments.
!      */
!     argp = *arg;
!     while (argcount < MAX_FUNC_ARGS - (funcexe->fe_partial == NULL ? 0
!                                              : funcexe->fe_partial->pt_argc))
      {
        // skip the '(' or ',' and possibly line breaks
        argp = skipwhite_and_linebreak(argp + 1, evalarg);
  
        if (*argp == ')' || *argp == ',' || *argp == NUL)
            break;
!       if (eval1(&argp, &argvars[argcount], evalarg) == FAIL)
        {
            ret = FAIL;
            break;
        }
!       ++argcount;
        // The comma should come right after the argument, but this wasn't
        // checked previously, thus only enforce it in Vim9 script.
        if (vim9script)
--- 1728,1763 ----
  }
  
  /*
!  * Get function arguments at "*arg" and advance it.
!  * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
   */
!     static int
! get_func_arguments(
!       char_u      **arg,
!       evalarg_T   *evalarg,
!       int         partial_argc,
!       typval_T    *argvars,
!       int         *argcount)
  {
!     char_u    *argp = *arg;
      int               ret = OK;
      int               vim9script = in_vim9script();
      int               evaluate = evalarg == NULL
                               ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
  
!     while (*argcount < MAX_FUNC_ARGS - partial_argc)
      {
        // skip the '(' or ',' and possibly line breaks
        argp = skipwhite_and_linebreak(argp + 1, evalarg);
  
        if (*argp == ')' || *argp == ',' || *argp == NUL)
            break;
!       if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL)
        {
            ret = FAIL;
            break;
        }
!       ++*argcount;
        // The comma should come right after the argument, but this wasn't
        // checked previously, thus only enforce it in Vim9 script.
        if (vim9script)
***************
*** 1791,1801 ****
--- 1783,1823 ----
            break;
        }
      }
+ 
      argp = skipwhite_and_linebreak(argp, evalarg);
      if (*argp == ')')
        ++argp;
      else
        ret = FAIL;
+     *arg = argp;
+     return ret;
+ }
+ 
+ /*
+  * Call a function and put the result in "rettv".
+  * Return OK or FAIL.
+  */
+     int
+ get_func_tv(
+     char_u    *name,          // name of the function
+     int               len,            // length of "name" or -1 to use 
strlen()
+     typval_T  *rettv,
+     char_u    **arg,          // argument, pointing to the '('
+     evalarg_T *evalarg,       // for line continuation
+     funcexe_T *funcexe)       // various values
+ {
+     char_u    *argp;
+     int               ret = OK;
+     typval_T  argvars[MAX_FUNC_ARGS + 1];     // vars for arguments
+     int               argcount = 0;                   // number of arguments 
found
+     int               vim9script = in_vim9script();
+     int               evaluate = evalarg == NULL
+                              ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
+ 
+     argp = *arg;
+     ret = get_func_arguments(&argp, evalarg,
+           (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc),
+                                                          argvars, &argcount);
  
      if (ret == OK)
      {
***************
*** 2884,2889 ****
--- 2906,2914 ----
        do_cmdline(NULL, get_func_line, (void *)fc,
                                     DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
  
+     // Invoke functions added with ":defer".
+     handle_defer();
+ 
      --RedrawingDisabled;
  
      // when the function was aborted because of an error, return -1
***************
*** 5457,5464 ****
--- 5482,5645 ----
      clear_evalarg(&evalarg, eap);
  }
  
+     static int
+ ex_call_inner(
+       exarg_T     *eap,
+       char_u      *name,
+       char_u      **arg,
+       char_u      *startarg,
+       funcexe_T  *funcexe_init,
+       evalarg_T   *evalarg)
+ {
+     linenr_T  lnum;
+     int               doesrange;
+     typval_T  rettv;
+     int               failed = FALSE;
+ 
+     /*
+      * When skipping, evaluate the function once, to find the end of the
+      * arguments.
+      * When the function takes a range, this is discovered after the first
+      * call, and the loop is broken.
+      */
+     if (eap->skip)
+     {
+       ++emsg_skip;
+       lnum = eap->line2;      // do it once, also with an invalid range
+     }
+     else
+       lnum = eap->line1;
+     for ( ; lnum <= eap->line2; ++lnum)
+     {
+       funcexe_T funcexe;
+ 
+       if (!eap->skip && eap->addr_count > 0)
+       {
+           if (lnum > curbuf->b_ml.ml_line_count)
+           {
+               // If the function deleted lines or switched to another buffer
+               // the line number may become invalid.
+               emsg(_(e_invalid_range));
+               break;
+           }
+           curwin->w_cursor.lnum = lnum;
+           curwin->w_cursor.col = 0;
+           curwin->w_cursor.coladd = 0;
+       }
+       *arg = startarg;
+ 
+       funcexe = *funcexe_init;
+       funcexe.fe_doesrange = &doesrange;
+       rettv.v_type = VAR_UNKNOWN;     // clear_tv() uses this
+       if (get_func_tv(name, -1, &rettv, arg, evalarg, &funcexe) == FAIL)
+       {
+           failed = TRUE;
+           break;
+       }
+       if (has_watchexpr())
+           dbg_check_breakpoint(eap);
+ 
+       // Handle a function returning a Funcref, Dictionary or List.
+       if (handle_subscript(arg, NULL, &rettv,
+                          eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
+       {
+           failed = TRUE;
+           break;
+       }
+ 
+       clear_tv(&rettv);
+       if (doesrange || eap->skip)
+           break;
+ 
+       // Stop when immediately aborting on error, or when an interrupt
+       // occurred or an exception was thrown but not caught.
+       // get_func_tv() returned OK, so that the check for trailing
+       // characters below is executed.
+       if (aborting())
+           break;
+     }
+     if (eap->skip)
+       --emsg_skip;
+     return failed;
+ }
+ 
+ /*
+  * Core part of ":defer func(arg)".  "arg" points to the "(" and is advanced.
+  * Returns FAIL or OK.
+  */
+     static int
+ ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
+ {
+     typval_T  argvars[MAX_FUNC_ARGS + 1];     // vars for arguments
+     int               argcount = 0;                   // number of arguments 
found
+     defer_T   *dr;
+     int               ret = FAIL;
+     char_u    *saved_name;
+ 
+     if (current_funccal == NULL)
+     {
+       semsg(_(e_str_not_inside_function), "defer");
+       return FAIL;
+     }
+     if (get_func_arguments(arg, evalarg, FALSE, argvars, &argcount) == FAIL)
+       goto theend;
+     saved_name = vim_strsave(name);
+     if (saved_name == NULL)
+       goto theend;
+ 
+     if (current_funccal->fc_defer.ga_itemsize == 0)
+       ga_init2(&current_funccal->fc_defer, sizeof(defer_T), 10);
+     if (ga_grow(&current_funccal->fc_defer, 1) == FAIL)
+       goto theend;
+     dr = ((defer_T *)current_funccal->fc_defer.ga_data)
+                                         + current_funccal->fc_defer.ga_len++;
+     dr->dr_name = saved_name;
+     dr->dr_argcount = argcount;
+     while (argcount > 0)
+     {
+       --argcount;
+       dr->dr_argvars[argcount] = argvars[argcount];
+     }
+     ret = OK;
+ 
+ theend:
+     while (--argcount >= 0)
+       clear_tv(&argvars[argcount]);
+     return ret;
+ }
+ 
+ /*
+  * Invoked after a functions has finished: invoke ":defer" functions.
+  */
+     void
+ handle_defer(void)
+ {
+     int           idx;
+ 
+     for (idx = current_funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
+     {
+       funcexe_T   funcexe;
+       typval_T    rettv;
+       defer_T     *dr = ((defer_T *)current_funccal->fc_defer.ga_data) + idx;
+       int         i;
+ 
+       CLEAR_FIELD(funcexe);
+       funcexe.fe_evaluate = TRUE;
+ 
+       rettv.v_type = VAR_UNKNOWN;     // clear_tv() uses this
+       call_func(dr->dr_name, -1, &rettv,
+                                   dr->dr_argcount, dr->dr_argvars, &funcexe);
+       clear_tv(&rettv);
+       vim_free(dr->dr_name);
+       for (i = dr->dr_argcount - 1; i >= 0; --i)
+           clear_tv(&dr->dr_argvars[i]);
+     }
+     ga_clear(&current_funccal->fc_defer);
+ }
+ 
  /*
   * ":1,25call func(arg1, arg2)"       function call.
+  * ":defer func(arg1, arg2)"    deferred function call.
   */
      void
  ex_call(exarg_T *eap)
***************
*** 5468,5476 ****
      char_u    *name;
      char_u    *tofree;
      int               len;
-     typval_T  rettv;
-     linenr_T  lnum;
-     int               doesrange;
      int               failed = FALSE;
      funcdict_T        fudi;
      partial_T *partial = NULL;
--- 5649,5654 ----
***************
*** 5482,5487 ****
--- 5660,5667 ----
      fill_evalarg_from_eap(&evalarg, eap, eap->skip);
      if (eap->skip)
      {
+       typval_T        rettv;
+ 
        // trans_function_name() doesn't work well when skipping, use eval0()
        // instead to skip to any following command, e.g. for:
        //   :if 0 | call dict.foo().bar() | endif
***************
*** 5531,5612 ****
        goto end;
      }
  
!     /*
!      * When skipping, evaluate the function once, to find the end of the
!      * arguments.
!      * When the function takes a range, this is discovered after the first
!      * call, and the loop is broken.
!      */
!     if (eap->skip)
      {
!       ++emsg_skip;
!       lnum = eap->line2;      // do it once, also with an invalid range
      }
      else
-       lnum = eap->line1;
-     for ( ; lnum <= eap->line2; ++lnum)
      {
        funcexe_T funcexe;
  
-       if (!eap->skip && eap->addr_count > 0)
-       {
-           if (lnum > curbuf->b_ml.ml_line_count)
-           {
-               // If the function deleted lines or switched to another buffer
-               // the line number may become invalid.
-               emsg(_(e_invalid_range));
-               break;
-           }
-           curwin->w_cursor.lnum = lnum;
-           curwin->w_cursor.col = 0;
-           curwin->w_cursor.coladd = 0;
-       }
-       arg = startarg;
- 
        CLEAR_FIELD(funcexe);
!       funcexe.fe_firstline = eap->line1;
!       funcexe.fe_lastline = eap->line2;
!       funcexe.fe_doesrange = &doesrange;
!       funcexe.fe_evaluate = !eap->skip;
        funcexe.fe_partial = partial;
        funcexe.fe_selfdict = fudi.fd_dict;
!       funcexe.fe_check_type = type;
        funcexe.fe_found_var = found_var;
!       rettv.v_type = VAR_UNKNOWN;     // clear_tv() uses this
!       if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
!       {
!           failed = TRUE;
!           break;
!       }
!       if (has_watchexpr())
!           dbg_check_breakpoint(eap);
! 
!       // Handle a function returning a Funcref, Dictionary or List.
!       if (handle_subscript(&arg, NULL, &rettv,
!                          eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
!       {
!           failed = TRUE;
!           break;
!       }
! 
!       clear_tv(&rettv);
!       if (doesrange || eap->skip)
!           break;
! 
!       // Stop when immediately aborting on error, or when an interrupt
!       // occurred or an exception was thrown but not caught.
!       // get_func_tv() returned OK, so that the check for trailing
!       // characters below is executed.
!       if (aborting())
!           break;
      }
-     if (eap->skip)
-       --emsg_skip;
  
      // When inside :try we need to check for following "| catch" or "| 
endtry".
      // Not when there was an error, but do check if an exception was thrown.
!     if ((!aborting() || did_throw)
!                                 && (!failed || eap->cstack->cs_trylevel > 0))
      {
        // Check for trailing illegal characters and a following command.
        arg = skipwhite(arg);
--- 5711,5739 ----
        goto end;
      }
  
!     if (eap->cmdidx == CMD_defer)
      {
!       arg = startarg;
!       failed = ex_defer_inner(name, &arg, &evalarg) == FAIL;
      }
      else
      {
        funcexe_T funcexe;
  
        CLEAR_FIELD(funcexe);
!       funcexe.fe_check_type = type;
        funcexe.fe_partial = partial;
        funcexe.fe_selfdict = fudi.fd_dict;
!       funcexe.fe_firstline = eap->line1;
!       funcexe.fe_lastline = eap->line2;
        funcexe.fe_found_var = found_var;
!       funcexe.fe_evaluate = !eap->skip;
!       failed = ex_call_inner(eap, name, &arg, startarg, &funcexe, &evalarg);
      }
  
      // When inside :try we need to check for following "| catch" or "| 
endtry".
      // Not when there was an error, but do check if an exception was thrown.
!     if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 
0))
      {
        // Check for trailing illegal characters and a following command.
        arg = skipwhite(arg);
*** ../vim-9.0.0369/src/proto/userfunc.pro      2022-06-29 12:54:48.068572061 
+0100
--- src/proto/userfunc.pro      2022-09-03 15:24:43.479993899 +0100
***************
*** 58,63 ****
--- 58,64 ----
  void func_ref(char_u *name);
  void func_ptr_ref(ufunc_T *fp);
  void ex_return(exarg_T *eap);
+ void handle_defer(void);
  void ex_call(exarg_T *eap);
  int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
  void discard_pending_return(void *rettv);
*** ../vim-9.0.0369/src/structs.h       2022-08-29 18:16:11.578636822 +0100
--- src/structs.h       2022-09-03 15:58:17.286657171 +0100
***************
*** 1753,1758 ****
--- 1753,1759 ----
      linenr_T  breakpoint;     // next line with breakpoint or zero
      int               dbg_tick;       // debug_tick when breakpoint was set
      int               level;          // top nesting level of executed 
function
+     garray_T  fc_defer;       // functions to be called on return
  #ifdef FEAT_PROFILE
      proftime_T        prof_child;     // time spent in a child
  #endif
***************
*** 1767,1772 ****
--- 1768,1781 ----
                                // "func"
  };
  
+ // structure used as item in "fc_defer"
+ typedef struct
+ {
+     char_u    *dr_name;       // function name, allocated
+     typval_T  dr_argvars[MAX_FUNC_ARGS + 1];
+     int               dr_argcount;
+ } defer_T;
+ 
  /*
   * Struct used by trans_function_name()
   */
***************
*** 2850,2856 ****
      int               b_u_synced;     // entry lists are synced
      long      b_u_seq_last;   // last used undo sequence number
      long      b_u_save_nr_last; // counter for last file write
!     long      b_u_seq_cur;    // hu_seq of header below which we are now
      time_T    b_u_time_cur;   // uh_time of header below which we are now
      long      b_u_save_nr_cur; // file write nr after which we are now
  
--- 2859,2865 ----
      int               b_u_synced;     // entry lists are synced
      long      b_u_seq_last;   // last used undo sequence number
      long      b_u_save_nr_last; // counter for last file write
!     long      b_u_seq_cur;    // uh_seq of header below which we are now
      time_T    b_u_time_cur;   // uh_time of header below which we are now
      long      b_u_save_nr_cur; // file write nr after which we are now
  
*** ../vim-9.0.0369/src/vim9compile.c   2022-09-03 10:52:18.395075356 +0100
--- src/vim9compile.c   2022-09-03 21:06:45.596800531 +0100
***************
*** 2373,2379 ****
                        r = generate_PUSHBLOB(cctx, blob_alloc());
                        break;
                    case VAR_FUNC:
!                       r = generate_PUSHFUNC(cctx, NULL, &t_func_void);
                        break;
                    case VAR_LIST:
                        r = generate_NEWLIST(cctx, 0, FALSE);
--- 2373,2379 ----
                        r = generate_PUSHBLOB(cctx, blob_alloc());
                        break;
                    case VAR_FUNC:
!                       r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
                        break;
                    case VAR_LIST:
                        r = generate_NEWLIST(cctx, 0, FALSE);
***************
*** 2748,2753 ****
--- 2748,2754 ----
            // Was compiled in this mode before: Free old instructions.
            delete_def_function_contents(dfunc, FALSE);
        ga_clear_strings(&dfunc->df_var_names);
+       dfunc->df_defer_var_idx = 0;
      }
      else
      {
***************
*** 3249,3254 ****
--- 3250,3259 ----
                    line = compile_eval(p, &cctx);
                    break;
  
+           case CMD_defer:
+                   line = compile_defer(p, &cctx);
+                   break;
+ 
            case CMD_echo:
            case CMD_echon:
            case CMD_echoconsole:
*** ../vim-9.0.0369/src/vim9cmds.c      2022-09-01 16:00:49.730496296 +0100
--- src/vim9cmds.c      2022-09-03 21:11:16.524703244 +0100
***************
*** 1654,1659 ****
--- 1654,1662 ----
      return p;
  }
  
+ /*
+  * Compile an expression or function call.
+  */
      char_u *
  compile_eval(char_u *arg, cctx_T *cctx)
  {
***************
*** 1682,1687 ****
--- 1685,1751 ----
  }
  
  /*
+  * Compile "defer func(arg)".
+  */
+     char_u *
+ compile_defer(char_u *arg_start, cctx_T *cctx)
+ {
+     char_u    *p;
+     char_u    *arg = arg_start;
+     int               argcount = 0;
+     dfunc_T   *dfunc;
+     type_T    *type;
+     int               func_idx;
+ 
+     // Get a funcref for the function name.
+     // TODO: better way to find the "(".
+     p = vim_strchr(arg, '(');
+     if (p == NULL)
+     {
+       semsg(_(e_missing_parenthesis_str), arg);
+       return NULL;
+     }
+     *p = NUL;
+     func_idx = find_internal_func(arg);
+     if (func_idx >= 0)
+       // TODO: better type
+       generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx),
+                                                          &t_func_any, FALSE);
+     else if (compile_expr0(&arg, cctx) == FAIL)
+       return NULL;
+     *p = '(';
+ 
+     // check for function type
+     type = get_type_on_stack(cctx, 0);
+     if (type->tt_type != VAR_FUNC)
+     {
+       emsg(_(e_function_name_required));
+       return NULL;
+     }
+ 
+     // compile the arguments
+     arg = skipwhite(p + 1);
+     if (compile_arguments(&arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
+       return NULL;
+ 
+     // TODO: check argument count with "type"
+ 
+     dfunc = ((dfunc_T *)def_functions.ga_data) + 
cctx->ctx_ufunc->uf_dfunc_idx;
+     if (dfunc->df_defer_var_idx == 0)
+     {
+       lvar_T *lvar = reserve_local(cctx, (char_u *)"@defer@", 7,
+                                                           TRUE, &t_list_any);
+       if (lvar == NULL)
+           return NULL;
+       dfunc->df_defer_var_idx = lvar->lv_idx + 1;
+     }
+     if (generate_DEFER(cctx, dfunc->df_defer_var_idx - 1, argcount) == FAIL)
+       return NULL;
+ 
+     return skipwhite(arg);
+ }
+ 
+ /*
   * compile "echo expr"
   * compile "echomsg expr"
   * compile "echoerr expr"
*** ../vim-9.0.0369/src/proto/vim9cmds.pro      2022-06-27 23:15:29.000000000 
+0100
--- src/proto/vim9cmds.pro      2022-09-03 19:27:40.826629675 +0100
***************
*** 21,26 ****
--- 21,27 ----
  char_u *compile_endtry(char_u *arg, cctx_T *cctx);
  char_u *compile_throw(char_u *arg, cctx_T *cctx);
  char_u *compile_eval(char_u *arg, cctx_T *cctx);
+ char_u *compile_defer(char_u *arg_start, cctx_T *cctx);
  char_u *compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx);
  char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
  char_u *compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx);
*** ../vim-9.0.0369/src/vim9.h  2022-09-01 16:00:49.726496301 +0100
--- src/vim9.h  2022-09-03 19:18:53.055031709 +0100
***************
*** 113,118 ****
--- 113,119 ----
      ISN_FUNCREF,    // push a function ref to dfunc isn_arg.funcref
      ISN_NEWFUNC,    // create a global function from a lambda function
      ISN_DEF,      // list functions
+     ISN_DEFER,            // :defer  argument count is isn_arg.number
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
***************
*** 419,424 ****
--- 420,431 ----
      int               dbg_break_lnum;     // first line to break after
  } debug_T;
  
+ // arguments to ISN_DEFER
+ typedef struct {
+     int               defer_var_idx;      // local variable index for defer 
list
+     int               defer_argcount;     // number of arguments
+ } deferins_T;
+ 
  /*
   * Instruction
   */
***************
*** 468,473 ****
--- 475,481 ----
        tobool_T            tobool;
        getitem_T           getitem;
        debug_T             debug;
+       deferins_T          defer;
      } isn_arg;
  };
  
***************
*** 498,503 ****
--- 506,514 ----
  
      int               df_varcount;        // number of local variables
      int               df_has_closure;     // one if a closure was created
+     int               df_defer_var_idx;   // index of local variable that has 
a list
+                                   // of deferred function calls; zero if not
+                                   // set
  };
  
  // Number of entries used by stack frame for a function call.
***************
*** 735,740 ****
--- 746,760 ----
                                    // lhs_name is not NULL
  };
  
+ /*
+  * List of special functions for "compile_arguments()".
+  */
+ typedef enum {
+     CA_NOT_SPECIAL,
+     CA_SEARCHPAIR,        // {skip} in searchpair() and searchpairpos()
+     CA_SUBSTITUTE,        // {sub} in substitute(), when prefixed with \=
+ } ca_special_T;
+ 
  // flags for typval2type()
  #define TVTT_DO_MEMBER            1
  #define TVTT_MORE_SPECIFIC  2 // get most specific type for member
*** ../vim-9.0.0369/src/vim9instr.c     2022-09-01 17:26:14.163194043 +0100
--- src/vim9instr.c     2022-09-03 21:08:12.132769664 +0100
***************
*** 616,622 ****
        case VAR_FUNC:
            if (tv->vval.v_string != NULL)
                iemsg("non-null function constant not supported");
!           generate_PUSHFUNC(cctx, NULL, &t_func_unknown);
            break;
        case VAR_PARTIAL:
            if (tv->vval.v_partial != NULL)
--- 616,622 ----
        case VAR_FUNC:
            if (tv->vval.v_string != NULL)
                iemsg("non-null function constant not supported");
!           generate_PUSHFUNC(cctx, NULL, &t_func_unknown, TRUE);
            break;
        case VAR_PARTIAL:
            if (tv->vval.v_partial != NULL)
***************
*** 796,804 ****
  
  /*
   * Generate an ISN_PUSHFUNC instruction with name "name".
   */
      int
! generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
  {
      isn_T     *isn;
      char_u    *funcname;
--- 796,806 ----
  
  /*
   * Generate an ISN_PUSHFUNC instruction with name "name".
+  * When "may_prefix" is TRUE prefix "g:" unless "name" is script-local or
+  * autoload.
   */
      int
! generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type, int may_prefix)
  {
      isn_T     *isn;
      char_u    *funcname;
***************
*** 808,814 ****
        return FAIL;
      if (name == NULL)
        funcname = NULL;
!     else if (*name == K_SPECIAL                                   // 
script-local
            || vim_strchr(name, AUTOLOAD_CHAR) != NULL)     // autoload
        funcname = vim_strsave(name);
      else
--- 810,817 ----
        return FAIL;
      if (name == NULL)
        funcname = NULL;
!     else if (!may_prefix
!           || *name == K_SPECIAL                           // script-local
            || vim_strchr(name, AUTOLOAD_CHAR) != NULL)     // autoload
        funcname = vim_strsave(name);
      else
***************
*** 1679,1684 ****
--- 1682,1703 ----
  }
  
  /*
+  * Generate an ISN_DEFER instruction.
+  */
+     int
+ generate_DEFER(cctx_T *cctx, int var_idx, int argcount)
+ {
+     isn_T *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr_drop(cctx, ISN_DEFER, argcount + 1)) == NULL)
+       return FAIL;
+     isn->isn_arg.defer.defer_var_idx = var_idx;
+     isn->isn_arg.defer.defer_argcount = argcount;
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_STRINGMEMBER instruction.
   */
      int
***************
*** 2240,2245 ****
--- 2259,2265 ----
        case ISN_CONCAT:
        case ISN_COND2BOOL:
        case ISN_DEBUG:
+       case ISN_DEFER:
        case ISN_DROP:
        case ISN_ECHO:
        case ISN_ECHOCONSOLE:
***************
*** 2296,2316 ****
        case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_SOURCE:
!     case ISN_STOREOUTER:
!     case ISN_STORERANGE:
!     case ISN_STOREREG:
!     case ISN_STOREV:
!     case ISN_STRINDEX:
!     case ISN_STRSLICE:
!     case ISN_THROW:
!     case ISN_TRYCONT:
!     case ISN_UNLETINDEX:
!     case ISN_UNLETRANGE:
!     case ISN_UNPACK:
!     case ISN_USEDICT:
        // nothing allocated
        break;
! }
  }
  
      void
--- 2316,2336 ----
        case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_SOURCE:
!       case ISN_STOREOUTER:
!       case ISN_STORERANGE:
!       case ISN_STOREREG:
!       case ISN_STOREV:
!       case ISN_STRINDEX:
!       case ISN_STRSLICE:
!       case ISN_THROW:
!       case ISN_TRYCONT:
!       case ISN_UNLETINDEX:
!       case ISN_UNLETRANGE:
!       case ISN_UNPACK:
!       case ISN_USEDICT:
        // nothing allocated
        break;
!     }
  }
  
      void
*** ../vim-9.0.0369/src/proto/vim9instr.pro     2022-06-27 23:15:30.000000000 
+0100
--- src/proto/vim9instr.pro     2022-09-03 21:06:35.156804223 +0100
***************
*** 23,29 ****
  int generate_PUSHCHANNEL(cctx_T *cctx);
  int generate_PUSHJOB(cctx_T *cctx);
  int generate_PUSHBLOB(cctx_T *cctx, blob_T *blob);
! int generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type);
  int generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type);
  int generate_GETITEM(cctx_T *cctx, int index, int with_op);
  int generate_SLICE(cctx_T *cctx, int count);
--- 23,29 ----
  int generate_PUSHCHANNEL(cctx_T *cctx);
  int generate_PUSHJOB(cctx_T *cctx);
  int generate_PUSHBLOB(cctx_T *cctx, blob_T *blob);
! int generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type, int 
may_prefix);
  int generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type);
  int generate_GETITEM(cctx_T *cctx, int index, int with_op);
  int generate_SLICE(cctx_T *cctx, int count);
***************
*** 52,57 ****
--- 52,58 ----
  int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
  int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
  int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, 
int at_top);
+ int generate_DEFER(cctx_T *cctx, int var_idx, int argcount);
  int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len);
  int generate_ECHO(cctx_T *cctx, int with_white, int count);
  int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);
*** ../vim-9.0.0369/src/vim9expr.c      2022-08-20 14:51:13.810510131 +0100
--- src/vim9expr.c      2022-09-03 21:07:27.052785757 +0100
***************
*** 313,319 ****
                // name.  If a '(' follows it must be a function.  Otherwise we
                // don't know, it can be "script.Func".
                if (cc == '(' || paren_follows_after_expr)
!                   res = generate_PUSHFUNC(cctx, auto_name, &t_func_any);
                else
                    res = generate_AUTOLOAD(cctx, auto_name, &t_any);
                vim_free(auto_name);
--- 313,319 ----
                // name.  If a '(' follows it must be a function.  Otherwise we
                // don't know, it can be "script.Func".
                if (cc == '(' || paren_follows_after_expr)
!                   res = generate_PUSHFUNC(cctx, auto_name, &t_func_any, TRUE);
                else
                    res = generate_AUTOLOAD(cctx, auto_name, &t_any);
                vim_free(auto_name);
***************
*** 329,335 ****
                    char_u sid_name[MAX_FUNC_NAME_LEN];
  
                    func_name_with_sid(exp_name, import->imp_sid, sid_name);
!                   res = generate_PUSHFUNC(cctx, sid_name, &t_func_any);
                }
                else
                    res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
--- 329,335 ----
                    char_u sid_name[MAX_FUNC_NAME_LEN];
  
                    func_name_with_sid(exp_name, import->imp_sid, sid_name);
!                   res = generate_PUSHFUNC(cctx, sid_name, &t_func_any, TRUE);
                }
                else
                    res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
***************
*** 353,359 ****
            if (ufunc != NULL)
            {
                // function call or function reference
!               generate_PUSHFUNC(cctx, ufunc->uf_name, NULL);
                return OK;
            }
            return FAIL;
--- 353,359 ----
            if (ufunc != NULL)
            {
                // function call or function reference
!               generate_PUSHFUNC(cctx, ufunc->uf_name, NULL, TRUE);
                return OK;
            }
            return FAIL;
***************
*** 387,393 ****
      if (func_needs_compiling(ufunc, compile_type)
              && compile_def_function(ufunc, TRUE, compile_type, NULL) == FAIL)
        return FAIL;
!     return generate_PUSHFUNC(cctx, ufunc->uf_name, ufunc->uf_func_type);
  }
  
  /*
--- 387,393 ----
      if (func_needs_compiling(ufunc, compile_type)
              && compile_def_function(ufunc, TRUE, compile_type, NULL) == FAIL)
        return FAIL;
!     return generate_PUSHFUNC(cctx, ufunc->uf_name, ufunc->uf_func_type, TRUE);
  }
  
  /*
***************
*** 610,628 ****
  }
  
  /*
-  * List of special functions for "compile_arguments".
-  */
- typedef enum {
-     CA_NOT_SPECIAL,
-     CA_SEARCHPAIR,        // {skip} in searchpair() and searchpairpos()
-     CA_SUBSTITUTE,        // {sub} in substitute(), when prefixed with \=
- } ca_special_T;
- 
- /*
   * Compile the argument expressions.
   * "arg" points to just after the "(" and is advanced to after the ")"
   */
!     static int
  compile_arguments(
        char_u       **arg,
        cctx_T       *cctx,
--- 610,619 ----
  }
  
  /*
   * Compile the argument expressions.
   * "arg" points to just after the "(" and is advanced to after the ")"
   */
!     int
  compile_arguments(
        char_u       **arg,
        cctx_T       *cctx,
*** ../vim-9.0.0369/src/proto/vim9expr.pro      2022-06-27 23:15:30.000000000 
+0100
--- src/proto/vim9expr.pro      2022-09-03 19:27:49.334623659 +0100
***************
*** 4,9 ****
--- 4,10 ----
  int compile_member(int is_slice, int *keeping_dict, cctx_T *cctx);
  int compile_load_scriptvar(cctx_T *cctx, char_u *name, char_u *start, char_u 
**end);
  int compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int is_expr, 
int error);
+ int compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, ca_special_T 
special_fn);
  char_u *to_name_end(char_u *arg, int use_namespace);
  char_u *to_name_const_end(char_u *arg);
  int get_lambda_tv_and_compile(char_u **arg, typval_T *rettv, int 
types_optional, evalarg_T *evalarg);
*** ../vim-9.0.0369/src/vim9execute.c   2022-09-01 16:00:49.730496296 +0100
--- src/vim9execute.c   2022-09-03 20:48:19.693212011 +0100
***************
*** 101,109 ****
--- 101,115 ----
  static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL};
  #endif
  
+ // Get pointer to item in the stack.
+ #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
+ 
  // Get pointer to item relative to the bottom of the stack, -1 is the last 
one.
  #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_stack.ga_len + (idx))
  
+ // Get pointer to a local variable on the stack.  Negative for arguments.
+ #define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
+ 
      void
  to_string_error(vartype_T vartype)
  {
***************
*** 610,618 ****
      return OK;
  }
  
- // Get pointer to item in the stack.
- #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
- 
  // Double linked list of funcstack_T in use.
  static funcstack_T *first_funcstack = NULL;
  
--- 616,621 ----
***************
*** 843,848 ****
--- 846,934 ----
  }
  
  /*
+  * Handle ISN_DEFER.  Stack has a function reference and "argcount" arguments.
+  * The local variable that lists deferred functions is "var_idx".
+  * Returns OK or FAIL.
+  */
+     static int
+ add_defer_func(int var_idx, int argcount, ectx_T *ectx)
+ {
+     typval_T  *defer_tv = STACK_TV_VAR(var_idx);
+     list_T    *defer_l;
+     typval_T  *func_tv;
+     list_T    *l;
+     int               i;
+     typval_T  listval;
+ 
+     if (defer_tv->v_type != VAR_LIST)
+     {
+       // first one, allocate the list
+       if (rettv_list_alloc(defer_tv) == FAIL)
+           return FAIL;
+     }
+     defer_l = defer_tv->vval.v_list;
+ 
+     l = list_alloc_with_items(argcount + 1);
+     if (l == NULL)
+       return FAIL;
+     listval.v_type = VAR_LIST;
+     listval.vval.v_list = l;
+     listval.v_lock = 0;
+     if (list_insert_tv(defer_l, &listval,
+                          defer_l == NULL ? NULL : defer_l->lv_first) == FAIL)
+     {
+       vim_free(l);
+       return FAIL;
+     }
+ 
+     func_tv = STACK_TV_BOT(-argcount - 1);
+     // TODO: check type is a funcref
+     list_set_item(l, 0, func_tv);
+ 
+     for (i = 1; i <= argcount; ++i)
+       list_set_item(l, i, STACK_TV_BOT(-argcount + i - 1));
+     ectx->ec_stack.ga_len -= argcount + 1;
+     return OK;
+ }
+ 
+ /*
+  * Invoked when returning from a function: Invoke any deferred calls.
+  */
+     static void
+ invoke_defer_funcs(ectx_T *ectx)
+ {
+     dfunc_T   *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                         + ectx->ec_dfunc_idx;
+     typval_T  *defer_tv = STACK_TV_VAR(dfunc->df_defer_var_idx - 1);
+     listitem_T        *li;
+ 
+     if (defer_tv->v_type != VAR_LIST)
+       return;  // no function added
+     for (li = defer_tv->vval.v_list->lv_first; li != NULL; li = li->li_next)
+     {
+       list_T      *l = li->li_tv.vval.v_list;
+       typval_T    rettv;
+       typval_T    argvars[MAX_FUNC_ARGS];
+       int         i;
+       listitem_T  *arg_li = l->lv_first;
+       funcexe_T   funcexe;
+ 
+       for (i = 0; i < l->lv_len - 1; ++i)
+       {
+           arg_li = arg_li->li_next;
+           argvars[i] = arg_li->li_tv;
+       }
+ 
+       CLEAR_FIELD(funcexe);
+       funcexe.fe_evaluate = TRUE;
+       rettv.v_type = VAR_UNKNOWN;
+       (void)call_func(l->lv_first->li_tv.vval.v_string, -1,
+                                    &rettv, l->lv_len - 1, argvars, &funcexe);
+       clear_tv(&rettv);
+     }
+ }
+ 
+ /*
   * Return from the current function.
   */
      static int
***************
*** 876,881 ****
--- 962,970 ----
      }
  #endif
  
+     if (dfunc->df_defer_var_idx > 0)
+       invoke_defer_funcs(ectx);
+ 
      // No check for uf_refcount being zero, cannot think of a way that would
      // happen.
      --dfunc->df_ufunc->uf_calls;
***************
*** 949,956 ****
      return OK;
  }
  
- #undef STACK_TV
- 
  /*
   * Prepare arguments and rettv for calling a builtin or user function.
   */
--- 1038,1043 ----
***************
*** 1732,1747 ****
      int               subs_status;
  } subs_expr_T;
  
- // Get pointer to item in the stack.
- #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
- 
- // Get pointer to item at the bottom of the stack, -1 is the bottom.
- #undef STACK_TV_BOT
- #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_stack.ga_len + idx)
- 
- // Get pointer to a local variable on the stack.  Negative for arguments.
- #define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + 
ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
- 
  // Set when calling do_debug().
  static ectx_T *debug_context = NULL;
  static int    debug_var_count;
--- 1819,1824 ----
***************
*** 3670,3675 ****
--- 3747,3759 ----
                }
                break;
  
+           // :defer func(arg)
+           case ISN_DEFER:
+               if (add_defer_func(iptr->isn_arg.defer.defer_var_idx,
+                            iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
+                   goto on_error;
+               break;
+ 
            // return from a :def function call without a value
            case ISN_RETURN_VOID:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
***************
*** 5024,5029 ****
--- 5108,5121 ----
  done:
      ret = OK;
  theend:
+     {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                         + ectx->ec_dfunc_idx;
+ 
+       if (dfunc->df_defer_var_idx > 0)
+           invoke_defer_funcs(ectx);
+     }
+ 
      dict_stack_clear(dict_stack_len_at_start);
      ectx->ec_trylevel_at_start = save_trylevel_at_start;
      return ret;
***************
*** 5903,5908 ****
--- 5995,6004 ----
            case ISN_PCALL_END:
                smsg("%s%4d PCALL end", pfx, current);
                break;
+           case ISN_DEFER:
+               smsg("%s%4d DEFER %d args", pfx, current,
+                                     (int)iptr->isn_arg.defer.defer_argcount);
+               break;
            case ISN_RETURN:
                smsg("%s%4d RETURN", pfx, current);
                break;
*** ../vim-9.0.0369/src/testdir/test_user_func.vim      2022-04-09 
10:59:49.000000000 +0100
--- src/testdir/test_user_func.vim      2022-09-03 21:28:20.276325774 +0100
***************
*** 529,532 ****
--- 529,564 ----
    bw!
  endfunc
  
+ func AddDefer(arg)
+   call extend(g:deferred, [a:arg])
+ endfunc
+ 
+ func WithDeferTwo()
+   call extend(g:deferred, ['in Two'])
+   for nr in range(3)
+     defer AddDefer('Two' .. nr)
+   endfor
+   call extend(g:deferred, ['end Two'])
+ endfunc
+ 
+ func WithDeferOne()
+   call extend(g:deferred, ['in One'])
+   call writefile(['text'], 'Xfuncdefer')
+   defer delete('Xfuncdefer')
+   defer AddDefer('One')
+   call WithDeferTwo()
+   call extend(g:deferred, ['end One'])
+ endfunc
+ 
+ func Test_defer()
+   let g:deferred = []
+   call WithDeferOne()
+ 
+   call assert_equal(['in One', 'in Two', 'end Two', 'Two2', 'Two1', 'Two0', 
'end One', 'One'], g:deferred)
+   unlet g:deferred
+ 
+   call assert_equal('', glob('Xfuncdefer'))
+ endfunc
+ 
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.0369/src/testdir/test_vim9_func.vim      2022-08-30 
18:42:13.170331381 +0100
--- src/testdir/test_vim9_func.vim      2022-09-03 21:22:34.784454129 +0100
***************
*** 4272,4277 ****
--- 4272,4307 ----
    v9.CheckScriptFailure(lines, 'E777', 2)
  enddef
  
+ def AddDefer(s: string)
+   g:deferred->extend([s])
+ enddef
+ 
+ def DeferTwo()
+   g:deferred->extend(['in Two'])
+   for n in range(3)
+     defer g:AddDefer('two' .. n)
+   endfor
+   g:deferred->extend(['end Two'])
+ enddef
+ 
+ def DeferOne()
+   g:deferred->extend(['in One'])
+   defer g:AddDefer('one')
+   g:DeferTwo()
+   g:deferred->extend(['end One'])
+ 
+   writefile(['text'], 'XdeferFile')
+   defer delete('XdeferFile')
+ enddef
+ 
+ def Test_defer()
+   g:deferred = []
+   g:DeferOne()
+   assert_equal(['in One', 'in Two', 'end Two', 'two2', 'two1', 'two0', 'end 
One', 'one'], g:deferred)
+   unlet g:deferred
+   assert_equal('', glob('XdeferFile'))
+ enddef
+ 
  " The following messes up syntax highlight, keep near the end.
  if has('python3')
    def Test_python3_command()
*** ../vim-9.0.0369/src/testdir/test_vim9_disassemble.vim       2022-09-01 
16:00:49.730496296 +0100
--- src/testdir/test_vim9_disassemble.vim       2022-09-03 21:12:37.848673708 
+0100
***************
*** 2900,2903 ****
--- 2900,2918 ----
                 '10 RETURN void', instr)
  enddef
  
+ def s:OneDefer()
+   defer delete("file")
+ enddef
+ 
+ def Test_disassemble_defer()
+   var instr = execute('disassemble s:OneDefer')
+   assert_match('OneDefer\_s*' ..
+         'defer delete("file")\_s*' ..
+         '\d PUSHFUNC "delete"\_s*' ..
+         '\d PUSHS "file"\_s*' ..
+         '\d DEFER 1 args\_s*' ..
+         '\d RETURN\_s*',
+         instr)
+ enddef
+ 
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-9.0.0369/src/version.c       2022-09-03 13:58:42.210028187 +0100
--- src/version.c       2022-09-03 21:28:53.004313588 +0100
***************
*** 709,710 ****
--- 709,712 ----
  {   /* Add new patch number below this line */
+ /**/
+     370,
  /**/

-- 
Git catch 22: "merge is not possible because you have unmerged files."

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220903203624.DFB251C0CE4%40moolenaar.net.

Raspunde prin e-mail lui