Patch 8.2.2677
Problem:    Vim9: cannot use only some of the default arguments.
Solution:   Use v:none to use default argument value.  Remove
            uf_def_arg_idx[], use JUMP_IF_ARG_SET. (closes #6504)
Files:      runtime/doc/vim9.txt, src/vim9compile.c, src/vim9execute.c,
            src/userfunc.c, src/structs.h, src/vim9.h,
            src/testdir/test_vim9_disassemble.vim,
            src/testdir/test_vim9_func.vim


*** ../vim-8.2.2676/runtime/doc/vim9.txt        2021-03-17 17:45:55.349935903 
+0100
--- runtime/doc/vim9.txt        2021-03-29 22:11:41.209266675 +0200
***************
*** 125,130 ****
--- 125,134 ----
        var name = value # comment
        var name = value# error!
  
+ Do not start a comment with #{, it looks like the legacy dictionary literal
+ and produces an error where this might be confusing.  #{{ or #{{{ are OK,
+ these can be used to start a fold.
+ 
  In legacy Vim script # is also used for the alternate file name.  In Vim9
  script you need to use %% instead.  Instead of ## use %%% (stands for all
  arguments).
***************
*** 164,169 ****
--- 168,182 ----
           for item in itemlist
             ...
  
+ When a function argument is optional (it has a default value) passing `v:none`
+ as the argument results in using the default value.  This is useful when you
+ want to specify a value for an argument that comes after an argument that
+ should use its default value.  Example: >
+       def MyFunc(one = 'one', last = 'last)
+         ...
+       enddef
+       MyFunc(v:none, 'LAST')  # first argument uses default value 'one'
+ 
  
  Functions and variables are script-local by default ~
                                                        *vim9-scopes*
***************
*** 190,195 ****
--- 203,214 ----
  However, it is recommended to always use "g:" to refer to a global function
  for clarity.
  
+ Since a script-local function reference can be used without "s:" the name must
+ start with an upper case letter even when using the ":s" prefix.  In legacy
+ script "s:funcref" could be used, because it could not be referred to with
+ "funcref".  In Vim9 script it can, therefore "s:Funcref" must be used to avoid
+ that the name interferes with builtin functions.
+ 
  In all cases the function must be defined before used.  That is when it is
  called, when `:defcompile` causes it to be compiled, or when code that calls
  it is being compiled (to figure out the return type).
***************
*** 279,284 ****
--- 298,306 ----
  variables, because they are not really declared.  They can also be deleted
  with `:unlet`.
  
+ `:lockvar` does not work on local variables.  Use `:const` and `:final`
+ instead.
+ 
  Variables, functions and function arguments cannot shadow previously defined
  or imported variables and functions in the same script file.
  Variables may shadow Ex commands, rename the variable if needed.
***************
*** 409,415 ****
                g:was_called = 'yes'
                return expression
            }
! NOT IMPLEMENTED YET
  
                                                        *vim9-curly*
  To avoid the "{" of a dictionary literal to be recognized as a statement block
--- 431,448 ----
                g:was_called = 'yes'
                return expression
            }
! 
! The ending "}" must be at the start of a line.  It can be followed by other
! characters, e.g.: >
!       var d = mapnew(dict, (k, v): string => {
!            return 'value'
!          })
! No command can follow the "{", only a comment can be used there.
! 
! Rationale: The "}" cannot be after a command because it would require parsing
! the commands to find it.  For consistency with that no command can follow the
! "{".  Unfortunately this means using "() => {  command  }" does not work, line
! breaks are always required.
  
                                                        *vim9-curly*
  To avoid the "{" of a dictionary literal to be recognized as a statement block
***************
*** 705,710 ****
--- 738,744 ----
  script this results in the string 'รก'.
  A negative index is counting from the end, "[-1]" is the last character.
  To exclude the last character use |slice()|.
+ To count composing characters separately use |strcharpart()|.
  If the index is out of range then an empty string results.
  
  In legacy script "++var" and "--var" would be silently accepted and have no
***************
*** 847,852 ****
--- 881,888 ----
  :enddef                       End of a function defined with `:def`. It 
should be on
                        a line by its own.
  
+ You may also find this wiki useful.  It was written by an early adoptor of
+ Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md
  
  If the script the function is defined in is Vim9 script, then script-local
  variables can be accessed without the "s:" prefix.  They must be defined
***************
*** 890,895 ****
--- 926,952 ----
          g/pattern/s/^/`=newText`/
        enddef
  
+ Closures defined in a loop will share the same context.  For example: >
+       var flist: list<func>
+       for i in range(10)
+         var inloop = i
+         flist[i] = () => inloop
+       endfor
+ 
+ The "inloop" variable will exist only once, all closures put in the list refer
+ to the same instance, which in the end will have the value 9.  This is
+ efficient.  If you do want a separate context for each closure call a function
+ to define it: >
+       def GetFunc(i: number): func
+         var inloop = i
+         return () => inloop
+       enddef
+ 
+       var flist: list<func>
+       for i in range(10)
+         flist[i] = GetFunc(i)
+       endfor
+ 
  ==============================================================================
  
  4. Types                                      *vim9-types*
*** ../vim-8.2.2676/src/vim9compile.c   2021-03-28 20:38:30.540591499 +0200
--- src/vim9compile.c   2021-03-29 22:06:12.234122708 +0200
***************
*** 1629,1634 ****
--- 1629,1650 ----
      return OK;
  }
  
+ /*
+  * Generate an ISN_JUMP_IF_ARG_SET instruction.
+  */
+     static int
+ generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr(cctx, ISN_JUMP_IF_ARG_SET)) == NULL)
+       return FAIL;
+     isn->isn_arg.jumparg.jump_arg_off = arg_off;
+     // jump_where is set later
+     return OK;
+ }
+ 
      static int
  generate_FOR(cctx_T *cctx, int loop_idx)
  {
***************
*** 1834,1839 ****
--- 1850,1862 ----
            type_T *expected;
            type_T *actual;
  
+           actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
+           if (actual == &t_special
+                             && i >= regular_args - ufunc->uf_def_args.ga_len)
+           {
+               // assume v:none used for default argument value
+               continue;
+           }
            if (i < regular_args)
            {
                if (ufunc->uf_arg_types == NULL)
***************
*** 1845,1851 ****
                expected = &t_any;
            else
                expected = ufunc->uf_va_type->tt_member;
-           actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
            if (need_type(actual, expected, -argcount + i, i + 1, cctx,
                                                          TRUE, FALSE) == FAIL)
            {
--- 1868,1873 ----
***************
*** 1961,1966 ****
--- 1983,1991 ----
                    if (varargs && i >= type->tt_argcount - 1)
                        expected = type->tt_args[
                                             type->tt_argcount - 1]->tt_member;
+                   else if (i >= type->tt_min_argcount
+                                                      && actual == &t_special)
+                       expected = &t_any;
                    else
                        expected = type->tt_args[i];
                    if (need_type(actual, expected, offset, i + 1,
***************
*** 8363,8374 ****
        int     did_set_arg_type = FALSE;
  
        // Produce instructions for the default values of optional arguments.
-       // Store the instruction index in uf_def_arg_idx[] so that we know
-       // where to start when the function is called, depending on the number
-       // of arguments.
-       ufunc->uf_def_arg_idx = ALLOC_CLEAR_MULT(int, count + 1);
-       if (ufunc->uf_def_arg_idx == NULL)
-           goto erret;
        SOURCING_LNUM = 0;  // line number unknown
        for (i = 0; i < count; ++i)
        {
--- 8388,8393 ----
***************
*** 8377,8387 ****
            int         arg_idx = first_def_arg + i;
            where_T     where;
            int         r;
  
            // Make sure later arguments are not found.
            ufunc->uf_args.ga_len = i;
  
-           ufunc->uf_def_arg_idx[i] = instr->ga_len;
            arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
            r = compile_expr0(&arg, &cctx);
  
--- 8396,8411 ----
            int         arg_idx = first_def_arg + i;
            where_T     where;
            int         r;
+           int         jump_instr_idx = instr->ga_len;
+           isn_T       *isn;
+ 
+           // Use a JUMP_IF_ARG_SET instruction to skip if the value was given.
+           if (generate_JUMP_IF_ARG_SET(&cctx, i - count - off) == FAIL)
+               goto erret;
  
            // Make sure later arguments are not found.
            ufunc->uf_args.ga_len = i;
  
            arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
            r = compile_expr0(&arg, &cctx);
  
***************
*** 8406,8413 ****
  
            if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
                goto erret;
        }
-       ufunc->uf_def_arg_idx[count] = instr->ga_len;
  
        if (did_set_arg_type)
            set_function_type(ufunc);
--- 8430,8440 ----
  
            if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
                goto erret;
+ 
+           // set instruction index in JUMP_IF_ARG_SET to here
+           isn = ((isn_T *)instr->ga_data) + jump_instr_idx;
+           isn->isn_arg.jumparg.jump_where = instr->ga_len;
        }
  
        if (did_set_arg_type)
            set_function_type(ufunc);
***************
*** 9114,9119 ****
--- 9141,9147 ----
        case ISN_FOR:
        case ISN_GETITEM:
        case ISN_JUMP:
+       case ISN_JUMP_IF_ARG_SET:
        case ISN_LISTAPPEND:
        case ISN_LISTINDEX:
        case ISN_LISTSLICE:
*** ../vim-8.2.2676/src/vim9execute.c   2021-03-26 20:41:24.773620612 +0100
--- src/vim9execute.c   2021-03-29 21:52:38.004281729 +0200
***************
*** 97,131 ****
  }
  
  /*
-  * Set the instruction index, depending on omitted arguments, where the 
default
-  * values are to be computed.  If all optional arguments are present, start
-  * with the function body.
-  * The expression evaluation is at the start of the instructions:
-  *  0 ->  EVAL default1
-  *           STORE arg[-2]
-  *  1 ->  EVAL default2
-  *           STORE arg[-1]
-  *  2 ->  function body
-  */
-     static void
- init_instr_idx(ufunc_T *ufunc, int argcount, ectx_T *ectx)
- {
-     if (ufunc->uf_def_args.ga_len == 0)
-       ectx->ec_iidx = 0;
-     else
-     {
-       int     defcount = ufunc->uf_args.ga_len - argcount;
- 
-       // If there is a varargs argument defcount can be negative, no defaults
-       // to evaluate then.
-       if (defcount < 0)
-           defcount = 0;
-       ectx->ec_iidx = ufunc->uf_def_arg_idx[
-                                        ufunc->uf_def_args.ga_len - defcount];
-     }
- }
- 
- /*
   * Create a new list from "count" items at the bottom of the stack.
   * When "count" is zero an empty list is added to the stack.
   */
--- 97,102 ----
***************
*** 363,370 ****
        current_sctx.sc_sid = ufunc->uf_script_ctx.sc_sid;
      }
  
!     // Decide where to start execution, handles optional arguments.
!     init_instr_idx(ufunc, argcount, ectx);
  
      return OK;
  }
--- 334,341 ----
        current_sctx.sc_sid = ufunc->uf_script_ctx.sc_sid;
      }
  
!     // Start execution at the first instruction.
!     ectx->ec_iidx = 0;
  
      return OK;
  }
***************
*** 1367,1377 ****
            && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
                                                                         ++idx)
      {
!       if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
!               && check_typval_arg_type(ufunc->uf_arg_types[idx], &argv[idx],
!                                                             idx + 1) == FAIL)
!           goto failed_early;
!       copy_tv(&argv[idx], STACK_TV_BOT(0));
        ++ectx.ec_stack.ga_len;
      }
  
--- 1338,1358 ----
            && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
                                                                         ++idx)
      {
!       if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
!               && argv[idx].v_type == VAR_SPECIAL
!               && argv[idx].vval.v_number == VVAL_NONE)
!       {
!           // Use the default value.
!           STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
!       }
!       else
!       {
!           if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
!                   && check_typval_arg_type(
!                       ufunc->uf_arg_types[idx], &argv[idx], idx + 1) == FAIL)
!               goto failed_early;
!           copy_tv(&argv[idx], STACK_TV_BOT(0));
!       }
        ++ectx.ec_stack.ga_len;
      }
  
***************
*** 1505,1512 ****
      where.wt_index = 0;
      where.wt_variable = FALSE;
  
!     // Decide where to start execution, handles optional arguments.
!     init_instr_idx(ufunc, argc, &ectx);
  
      for (;;)
      {
--- 1486,1493 ----
      where.wt_index = 0;
      where.wt_variable = FALSE;
  
!     // Start execution at the first instruction.
!     ectx.ec_iidx = 0;
  
      for (;;)
      {
***************
*** 2738,2743 ****
--- 2719,2734 ----
                }
                break;
  
+           // Jump if an argument with a default value was already set and not
+           // v:none.
+           case ISN_JUMP_IF_ARG_SET:
+               tv = STACK_TV_VAR(iptr->isn_arg.jumparg.jump_arg_off);
+               if (tv->v_type != VAR_UNKNOWN
+                       && !(tv->v_type == VAR_SPECIAL
+                                           && tv->vval.v_number == VVAL_NONE))
+                   ectx.ec_iidx = iptr->isn_arg.jumparg.jump_where;
+               break;
+ 
            // top of a for loop
            case ISN_FOR:
                {
***************
*** 4517,4522 ****
--- 4508,4519 ----
                }
                break;
  
+           case ISN_JUMP_IF_ARG_SET:
+               smsg("%4d JUMP_IF_ARG_SET arg[%d] -> %d", current,
+                        iptr->isn_arg.jumparg.jump_arg_off + STACK_FRAME_SIZE,
+                                               iptr->isn_arg.jump.jump_where);
+               break;
+ 
            case ISN_FOR:
                {
                    forloop_T *forloop = &iptr->isn_arg.forloop;
*** ../vim-8.2.2676/src/userfunc.c      2021-03-27 15:40:07.979976135 +0100
--- src/userfunc.c      2021-03-28 21:56:22.752634642 +0200
***************
*** 1914,1920 ****
      ga_clear_strings(&(fp->uf_def_args));
      ga_clear_strings(&(fp->uf_lines));
      VIM_CLEAR(fp->uf_arg_types);
-     VIM_CLEAR(fp->uf_def_arg_idx);
      VIM_CLEAR(fp->uf_block_ids);
      VIM_CLEAR(fp->uf_va_name);
      clear_type_list(&fp->uf_type_list);
--- 1914,1919 ----
***************
*** 2049,2062 ****
        mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types,
                                    sizeof(type_T *) * fp->uf_args.ga_len);
      }
-     if (ufunc->uf_def_arg_idx != NULL)
-     {
-       fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1);
-       if (fp->uf_def_arg_idx == NULL)
-           goto failed;
-       mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx,
-                                sizeof(int) * fp->uf_def_args.ga_len + 1);
-     }
      if (ufunc->uf_va_name != NULL)
      {
        fp->uf_va_name = vim_strsave(ufunc->uf_va_name);
--- 2048,2053 ----
*** ../vim-8.2.2676/src/structs.h       2021-03-21 22:12:31.448826619 +0100
--- src/structs.h       2021-03-28 22:01:11.331474367 +0200
***************
*** 1607,1614 ****
      type_T    **uf_arg_types; // argument types (count == uf_args.ga_len)
      type_T    *uf_ret_type;   // return type
      garray_T  uf_type_list;   // types used in arg and return types
-     int               *uf_def_arg_idx; // instruction indexes for evaluating
-                               // uf_def_args; length: uf_def_args.ga_len + 1
      partial_T *uf_partial;    // for closure created inside :def function:
                                // information about the context
  
--- 1607,1612 ----
*** ../vim-8.2.2676/src/vim9.h  2021-03-24 22:00:52.042056113 +0100
--- src/vim9.h  2021-03-28 22:03:55.790858534 +0200
***************
*** 92,97 ****
--- 92,98 ----
  
      // expression operations
      ISN_JUMP,     // jump if condition is matched isn_arg.jump
+     ISN_JUMP_IF_ARG_SET, // jump if argument is already set, uses 
isn_arg.jumparg
  
      // loop
      ISN_FOR,      // get next item from a list, uses isn_arg.forloop
***************
*** 203,208 ****
--- 204,215 ----
      int               jump_where;         // position to jump to
  } jump_T;
  
+ // arguments to ISN_JUMP_IF_ARG_SET
+ typedef struct {
+     int               jump_arg_off;       // argument index, negative
+     int               jump_where;         // position to jump to
+ } jumparg_T;
+ 
  // arguments to ISN_FOR
  typedef struct {
      int           for_idx;        // loop variable index
***************
*** 346,351 ****
--- 353,359 ----
        job_T               *job;
        partial_T           *partial;
        jump_T              jump;
+       jumparg_T           jumparg;
        forloop_T           forloop;
        try_T               try;
        trycont_T           trycont;
*** ../vim-8.2.2676/src/testdir/test_vim9_disassemble.vim       2021-03-26 
20:41:24.773620612 +0100
--- src/testdir/test_vim9_disassemble.vim       2021-03-28 22:37:46.539166248 
+0200
***************
*** 641,658 ****
  enddef
  
  
! def FuncWithDefault(arg: string = 'default'): string
!   return arg
  enddef
  
  def Test_disassemble_call_default()
    var res = execute('disass FuncWithDefault')
    assert_match('FuncWithDefault\_s*' ..
          '\d PUSHS "default"\_s*' ..
          '\d STORE arg\[-1]\_s*' ..
!         'return arg\_s*' ..
          '\d LOAD arg\[-1]\_s*' ..
!         '\d RETURN',
          res)
  enddef
  
--- 641,665 ----
  enddef
  
  
! def FuncWithDefault(arg: string = 'default', nr = 77): string
!   return arg .. nr
  enddef
  
  def Test_disassemble_call_default()
    var res = execute('disass FuncWithDefault')
    assert_match('FuncWithDefault\_s*' ..
+         '\d JUMP_IF_ARG_SET arg\[-2\] -> 3\_s*' ..
          '\d PUSHS "default"\_s*' ..
+         '\d STORE arg\[-2]\_s*' ..
+         '3 JUMP_IF_ARG_SET arg\[-1\] -> 6\_s*' ..
+         '\d PUSHNR 77\_s*' ..
          '\d STORE arg\[-1]\_s*' ..
!         'return arg .. nr\_s*' ..
!         '6 LOAD arg\[-2]\_s*' ..
          '\d LOAD arg\[-1]\_s*' ..
!         '\d 2STRING stack\[-1]\_s*' ..
!         '\d\+ CONCAT\_s*' ..
!         '\d\+ RETURN',
          res)
  enddef
  
*** ../vim-8.2.2676/src/testdir/test_vim9_func.vim      2021-03-24 
22:00:52.042056113 +0100
--- src/testdir/test_vim9_func.vim      2021-03-29 21:58:20.319362951 +0200
***************
*** 308,328 ****
    return second ? name : 'none'
  enddef
  
  def Test_call_default_args()
    MyDefaultArgs()->assert_equal('string')
    MyDefaultArgs('one')->assert_equal('one')
!   assert_fails('MyDefaultArgs("one", "two")', 'E118:', '', 3, 
'Test_call_default_args')
  
    MyDefaultSecond('test')->assert_equal('test')
    MyDefaultSecond('test', true)->assert_equal('test')
    MyDefaultSecond('test', false)->assert_equal('none')
  
    CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef', 
'defcompile'], 'E1001:')
    delfunc g:Func
    CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 
'defcompile'], 'E1013: Argument 1: type mismatch, expected number but got 
string')
    delfunc g:Func
  
!   var lines =<< trim END
        vim9script
        def Func(a = b == 0 ? 1 : 2, b = 0)
        enddef
--- 308,345 ----
    return second ? name : 'none'
  enddef
  
+ 
  def Test_call_default_args()
    MyDefaultArgs()->assert_equal('string')
+   MyDefaultArgs(v:none)->assert_equal('string')
    MyDefaultArgs('one')->assert_equal('one')
!   assert_fails('MyDefaultArgs("one", "two")', 'E118:', '', 4, 
'Test_call_default_args')
  
    MyDefaultSecond('test')->assert_equal('test')
    MyDefaultSecond('test', true)->assert_equal('test')
    MyDefaultSecond('test', false)->assert_equal('none')
  
+   var lines =<< trim END
+       def MyDefaultThird(name: string, aa = 'aa', bb = 'bb'): string
+         return name .. aa .. bb
+       enddef
+ 
+       MyDefaultThird('->')->assert_equal('->aabb')
+       MyDefaultThird('->', v:none)->assert_equal('->aabb')
+       MyDefaultThird('->', 'xx')->assert_equal('->xxbb')
+       MyDefaultThird('->', v:none, v:none)->assert_equal('->aabb')
+       MyDefaultThird('->', 'xx', v:none)->assert_equal('->xxbb')
+       MyDefaultThird('->', v:none, 'yy')->assert_equal('->aayy')
+       MyDefaultThird('->', 'xx', 'yy')->assert_equal('->xxyy')
+   END
+   CheckDefAndScriptSuccess(lines)
+ 
    CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef', 
'defcompile'], 'E1001:')
    delfunc g:Func
    CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 
'defcompile'], 'E1013: Argument 1: type mismatch, expected number but got 
string')
    delfunc g:Func
  
!   lines =<< trim END
        vim9script
        def Func(a = b == 0 ? 1 : 2, b = 0)
        enddef
*** ../vim-8.2.2676/src/version.c       2021-03-29 21:06:01.171475448 +0200
--- src/version.c       2021-03-29 22:07:08.389976243 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2677,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
36. You miss more than five meals a week downloading the latest games from
    Apogee.

 /// 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/202103292015.12TKFMGf1241005%40masaka.moolenaar.net.

Raspunde prin e-mail lui