Patch 8.2.1956
Problem:    Vim9: cannot specify argument types for lambda.
Solution:   Allow adding argument types.  Check arguments when calling a
            function reference.
Files:      src/userfunc.c, src/proto/userfunc.pro, src/vim9compile.c,
            src/eval.c, src/testdir/test_vim9_disassemble.vim,
            src/testdir/test_vim9_func.vim


*** ../vim-8.2.1955/src/userfunc.c      2020-10-20 14:25:03.930883006 +0200
--- src/userfunc.c      2020-11-05 18:08:44.696737953 +0100
***************
*** 54,64 ****
  /*
   * Get one function argument.
   * If "argtypes" is not NULL also get the type: "arg: type".
   * Return a pointer to after the type.
   * When something is wrong return "arg".
   */
      static char_u *
! one_function_arg(char_u *arg, garray_T *newargs, garray_T *argtypes, int skip)
  {
      char_u    *p = arg;
      char_u    *arg_copy = NULL;
--- 54,70 ----
  /*
   * Get one function argument.
   * If "argtypes" is not NULL also get the type: "arg: type".
+  * If "types_optional" is TRUE a missing type is OK, use "any".
   * Return a pointer to after the type.
   * When something is wrong return "arg".
   */
      static char_u *
! one_function_arg(
!       char_u      *arg,
!       garray_T    *newargs,
!       garray_T    *argtypes,
!       int         types_optional,
!       int         skip)
  {
      char_u    *p = arg;
      char_u    *arg_copy = NULL;
***************
*** 105,111 ****
      }
  
      // get any type from "arg: type"
!     if (argtypes != NULL && ga_grow(argtypes, 1) == OK)
      {
        char_u *type = NULL;
  
--- 111,117 ----
      }
  
      // get any type from "arg: type"
!     if (argtypes != NULL && (skip || ga_grow(argtypes, 1) == OK))
      {
        char_u *type = NULL;
  
***************
*** 118,139 ****
        if (*p == ':')
        {
            ++p;
!           if (!VIM_ISWHITE(*p))
            {
                semsg(_(e_white_space_required_after_str), ":");
                return arg;
            }
            type = skipwhite(p);
            p = skip_type(type, TRUE);
!           type = vim_strnsave(type, p - type);
        }
!       else if (*skipwhite(p) != '=')
        {
            semsg(_(e_missing_argument_type_for_str),
                                            arg_copy == NULL ? arg : arg_copy);
            return arg;
        }
!       ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
      }
  
      return p;
--- 124,152 ----
        if (*p == ':')
        {
            ++p;
!           if (!skip && !VIM_ISWHITE(*p))
            {
                semsg(_(e_white_space_required_after_str), ":");
                return arg;
            }
            type = skipwhite(p);
            p = skip_type(type, TRUE);
!           if (!skip)
!               type = vim_strnsave(type, p - type);
        }
!       else if (*skipwhite(p) != '=' && !types_optional)
        {
            semsg(_(e_missing_argument_type_for_str),
                                            arg_copy == NULL ? arg : arg_copy);
            return arg;
        }
!       if (!skip)
!       {
!           if (type == NULL && types_optional)
!               // lambda arguments default to "any" type
!               type = vim_strsave((char_u *)"any");
!           ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
!       }
      }
  
      return p;
***************
*** 148,153 ****
--- 161,167 ----
      char_u    endchar,
      garray_T  *newargs,
      garray_T  *argtypes,      // NULL unless using :def
+     int               types_optional, // types optional if "argtypes" is not 
NULL
      int               *varargs,
      garray_T  *default_args,
      int               skip,
***************
*** 196,202 ****
        {
            if (!skip)
                semsg(_(e_invarg2), *argp);
!           break;
        }
        if (*p == endchar)
            break;
--- 210,216 ----
        {
            if (!skip)
                semsg(_(e_invarg2), *argp);
!           goto err_ret;
        }
        if (*p == endchar)
            break;
***************
*** 213,224 ****
                // ...name: list<type>
                if (!eval_isnamec1(*p))
                {
!                   emsg(_(e_missing_name_after_dots));
!                   break;
                }
  
                arg = p;
!               p = one_function_arg(p, newargs, argtypes, skip);
                if (p == arg)
                    break;
            }
--- 227,240 ----
                // ...name: list<type>
                if (!eval_isnamec1(*p))
                {
!                   if (!skip)
!                       emsg(_(e_missing_name_after_dots));
!                   goto err_ret;
                }
  
                arg = p;
!               p = one_function_arg(p, newargs, argtypes, types_optional,
!                                                                        skip);
                if (p == arg)
                    break;
            }
***************
*** 226,232 ****
        else
        {
            arg = p;
!           p = one_function_arg(p, newargs, argtypes, skip);
            if (p == arg)
                break;
  
--- 242,248 ----
        else
        {
            arg = p;
!           p = one_function_arg(p, newargs, argtypes, types_optional, skip);
            if (p == arg)
                break;
  
***************
*** 304,309 ****
--- 320,382 ----
  }
  
  /*
+  * Parse the argument types, filling "fp->uf_arg_types".
+  * Return OK or FAIL.
+  */
+     static int
+ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
+ {
+     ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
+     if (argtypes->ga_len > 0)
+     {
+       // When "varargs" is set the last name/type goes into uf_va_name
+       // and uf_va_type.
+       int len = argtypes->ga_len - (varargs ? 1 : 0);
+ 
+       if (len > 0)
+           fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
+       if (fp->uf_arg_types != NULL)
+       {
+           int i;
+           type_T      *type;
+ 
+           for (i = 0; i < len; ++ i)
+           {
+               char_u *p = ((char_u **)argtypes->ga_data)[i];
+ 
+               if (p == NULL)
+                   // will get the type from the default value
+                   type = &t_unknown;
+               else
+                   type = parse_type(&p, &fp->uf_type_list);
+               if (type == NULL)
+                   return FAIL;
+               fp->uf_arg_types[i] = type;
+           }
+       }
+       if (varargs)
+       {
+           char_u *p;
+ 
+           // Move the last argument "...name: type" to uf_va_name and
+           // uf_va_type.
+           fp->uf_va_name = ((char_u **)fp->uf_args.ga_data)
+                                                 [fp->uf_args.ga_len - 1];
+           --fp->uf_args.ga_len;
+           p = ((char_u **)argtypes->ga_data)[len];
+           if (p == NULL)
+               // todo: get type from default value
+               fp->uf_va_type = &t_any;
+           else
+               fp->uf_va_type = parse_type(&p, &fp->uf_type_list);
+           if (fp->uf_va_type == NULL)
+               return FAIL;
+       }
+     }
+     return OK;
+ }
+ 
+ /*
   * Register function "fp" as using "current_funccal" as its scope.
   */
      static int
***************
*** 386,401 ****
  
  /*
   * Parse a lambda expression and get a Funcref from "*arg".
   * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
   */
      int
! get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
  {
      int               evaluate = evalarg != NULL
                                      && (evalarg->eval_flags & EVAL_EVALUATE);
      garray_T  newargs;
      garray_T  newlines;
      garray_T  *pnewargs;
      ufunc_T   *fp = NULL;
      partial_T   *pt = NULL;
      int               varargs;
--- 459,480 ----
  
  /*
   * Parse a lambda expression and get a Funcref from "*arg".
+  * When "types_optional" is TRUE optionally take argument types.
   * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
   */
      int
! get_lambda_tv(
!       char_u      **arg,
!       typval_T    *rettv,
!       int         types_optional,
!       evalarg_T   *evalarg)
  {
      int               evaluate = evalarg != NULL
                                      && (evalarg->eval_flags & EVAL_EVALUATE);
      garray_T  newargs;
      garray_T  newlines;
      garray_T  *pnewargs;
+     garray_T  argtypes;
      ufunc_T   *fp = NULL;
      partial_T   *pt = NULL;
      int               varargs;
***************
*** 411,418 ****
  
      // First, check if this is a lambda expression. "->" must exist.
      s = skipwhite(*arg + 1);
!     ret = get_function_args(&s, '-', NULL, NULL, NULL, NULL, TRUE,
!                                                                  NULL, NULL);
      if (ret == FAIL || *s != '>')
        return NOTDONE;
  
--- 490,498 ----
  
      // First, check if this is a lambda expression. "->" must exist.
      s = skipwhite(*arg + 1);
!     ret = get_function_args(&s, '-', NULL,
!           types_optional ? &argtypes : NULL, types_optional,
!                                                NULL, NULL, TRUE, NULL, NULL);
      if (ret == FAIL || *s != '>')
        return NOTDONE;
  
***************
*** 422,430 ****
      else
        pnewargs = NULL;
      *arg = skipwhite(*arg + 1);
!     // TODO: argument types
!     ret = get_function_args(arg, '-', pnewargs, NULL, &varargs, NULL, FALSE,
!                                                                  NULL, NULL);
      if (ret == FAIL || **arg != '>')
        goto errret;
  
--- 502,510 ----
      else
        pnewargs = NULL;
      *arg = skipwhite(*arg + 1);
!     ret = get_function_args(arg, '-', pnewargs,
!           types_optional ? &argtypes : NULL, types_optional,
!                                           &varargs, NULL, FALSE, NULL, NULL);
      if (ret == FAIL || **arg != '>')
        goto errret;
  
***************
*** 489,494 ****
--- 569,578 ----
        hash_add(&func_hashtab, UF2HIKEY(fp));
        fp->uf_args = newargs;
        ga_init(&fp->uf_def_args);
+       if (types_optional
+                        && parse_argument_types(fp, &argtypes, FALSE) == FAIL)
+           goto errret;
+ 
        fp->uf_lines = newlines;
        if (current_funccal != NULL && eval_lavars)
        {
***************
*** 523,533 ****
--- 607,621 ----
        evalarg->eval_tofree = tofree;
      else
        vim_free(tofree);
+     if (types_optional)
+       ga_clear_strings(&argtypes);
      return OK;
  
  errret:
      ga_clear_strings(&newargs);
      ga_clear_strings(&newlines);
+     if (types_optional)
+       ga_clear_strings(&argtypes);
      vim_free(fp);
      vim_free(pt);
      if (evalarg != NULL && evalarg->eval_tofree == NULL)
***************
*** 2907,2913 ****
      // This may get more lines and make the pointers into the first line
      // invalid.
      if (get_function_args(&p, ')', &newargs,
!                       eap->cmdidx == CMD_def ? &argtypes : NULL,
                         &varargs, &default_args, eap->skip,
                         eap, &line_to_free) == FAIL)
        goto errret_2;
--- 2995,3001 ----
      // This may get more lines and make the pointers into the first line
      // invalid.
      if (get_function_args(&p, ')', &newargs,
!                       eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
                         &varargs, &default_args, eap->skip,
                         eap, &line_to_free) == FAIL)
        goto errret_2;
***************
*** 3502,3559 ****
                cstack->cs_flags[i] |= CSF_FUNC_DEF;
        }
  
!       // parse the argument types
!       ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
!       if (argtypes.ga_len > 0)
!       {
!           // When "varargs" is set the last name/type goes into uf_va_name
!           // and uf_va_type.
!           int len = argtypes.ga_len - (varargs ? 1 : 0);
! 
!           if (len > 0)
!               fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
!           if (fp->uf_arg_types != NULL)
!           {
!               int     i;
!               type_T  *type;
! 
!               for (i = 0; i < len; ++ i)
!               {
!                   p = ((char_u **)argtypes.ga_data)[i];
!                   if (p == NULL)
!                       // will get the type from the default value
!                       type = &t_unknown;
!                   else
!                       type = parse_type(&p, &fp->uf_type_list);
!                   if (type == NULL)
!                   {
!                       SOURCING_LNUM = lnum_save;
!                       goto errret_2;
!                   }
!                   fp->uf_arg_types[i] = type;
!               }
!           }
!           if (varargs)
!           {
!               // Move the last argument "...name: type" to uf_va_name and
!               // uf_va_type.
!               fp->uf_va_name = ((char_u **)fp->uf_args.ga_data)
!                                                     [fp->uf_args.ga_len - 1];
!               --fp->uf_args.ga_len;
!               p = ((char_u **)argtypes.ga_data)[len];
!               if (p == NULL)
!                   // todo: get type from default value
!                   fp->uf_va_type = &t_any;
!               else
!                   fp->uf_va_type = parse_type(&p, &fp->uf_type_list);
!               if (fp->uf_va_type == NULL)
!               {
!                   SOURCING_LNUM = lnum_save;
!                   goto errret_2;
!               }
!           }
!           varargs = FALSE;
        }
  
        // parse the return type, if any
        if (ret_type == NULL)
--- 3590,3601 ----
                cstack->cs_flags[i] |= CSF_FUNC_DEF;
        }
  
!       if (parse_argument_types(fp, &argtypes, varargs) == FAIL)
!       {
!           SOURCING_LNUM = lnum_save;
!           goto errret_2;
        }
+       varargs = FALSE;
  
        // parse the return type, if any
        if (ret_type == NULL)
*** ../vim-8.2.1955/src/proto/userfunc.pro      2020-10-15 12:46:38.733199522 
+0200
--- src/proto/userfunc.pro      2020-11-04 20:18:35.449766231 +0100
***************
*** 1,10 ****
  /* userfunc.c */
  void func_init(void);
  hashtab_T *func_tbl_get(void);
! int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, 
garray_T *argtypes, int *varargs, garray_T *default_args, int skip, exarg_T 
*eap, char_u **line_to_free);
  char_u *get_lambda_name(void);
  char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
! int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
  char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int 
no_autoload);
  void emsg_funcname(char *ermsg, char_u *name);
  int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, 
evalarg_T *evalarg, funcexe_T *funcexe);
--- 1,10 ----
  /* userfunc.c */
  void func_init(void);
  hashtab_T *func_tbl_get(void);
! int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, 
garray_T *argtypes, int types_optional, int *varargs, garray_T *default_args, 
int skip, exarg_T *eap, char_u **line_to_free);
  char_u *get_lambda_name(void);
  char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
! int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, 
evalarg_T *evalarg);
  char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int 
no_autoload);
  void emsg_funcname(char *ermsg, char_u *name);
  int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, 
evalarg_T *evalarg, funcexe_T *funcexe);
*** ../vim-8.2.1955/src/vim9compile.c   2020-11-01 17:03:34.215513804 +0100
--- src/vim9compile.c   2020-11-05 18:38:55.388002674 +0100
***************
*** 1695,1700 ****
--- 1695,1724 ----
                semsg(_(e_toomanyarg), name);
                return FAIL;
            }
+           if (type->tt_args != NULL)
+           {
+               int i;
+ 
+               for (i = 0; i < argcount; ++i)
+               {
+                   int     offset = -argcount + i - 1;
+                   type_T *actual = ((type_T **)stack->ga_data)[
+                                                      stack->ga_len + offset];
+                   type_T *expected;
+ 
+                   if (varargs && i >= type->tt_min_argcount - 1)
+                       expected = type->tt_args[
+                                        type->tt_min_argcount - 1]->tt_member;
+                   else
+                       expected = type->tt_args[i];
+                   if (need_type(actual, expected, offset,
+                                                   cctx, TRUE, FALSE) == FAIL)
+                   {
+                       arg_type_mismatch(expected, actual, i + 1);
+                       return FAIL;
+                   }
+               }
+           }
        }
        ret_type = type->tt_member;
      }
***************
*** 2835,2841 ****
      evalarg.eval_cctx = cctx;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, &evalarg) != OK)
      {
        clear_evalarg(&evalarg, NULL);
        return FAIL;
--- 2859,2865 ----
      evalarg.eval_cctx = cctx;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, TRUE, &evalarg) != OK)
      {
        clear_evalarg(&evalarg, NULL);
        return FAIL;
***************
*** 2844,2850 ****
      ufunc = rettv.vval.v_partial->pt_func;
      ++ufunc->uf_refcount;
      clear_tv(&rettv);
-     ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
  
      // The function will have one line: "return {expr}".
      // Compile it into instructions.
--- 2868,2873 ----
***************
*** 2880,2886 ****
      int               ret = FAIL;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) == FAIL)
        return FAIL;
  
      if (**arg != '(')
--- 2903,2909 ----
      int               ret = FAIL;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, TRUE, &EVALARG_EVALUATE) == FAIL)
        return FAIL;
  
      if (**arg != '(')
***************
*** 3796,3805 ****
         */
        case '{':   {
                        char_u *start = skipwhite(*arg + 1);
  
                        // Find out what comes after the arguments.
                        ret = get_function_args(&start, '-', NULL,
!                                          NULL, NULL, NULL, TRUE, NULL, NULL);
                        if (ret != FAIL && *start == '>')
                            ret = compile_lambda(arg, cctx);
                        else
--- 3819,3830 ----
         */
        case '{':   {
                        char_u *start = skipwhite(*arg + 1);
+                       garray_T ga_arg;
  
                        // Find out what comes after the arguments.
                        ret = get_function_args(&start, '-', NULL,
!                                       &ga_arg, TRUE, NULL, NULL,
!                                                            TRUE, NULL, NULL);
                        if (ret != FAIL && *start == '>')
                            ret = compile_lambda(arg, cctx);
                        else
*** ../vim-8.2.1955/src/eval.c  2020-10-28 13:53:46.549128959 +0100
--- src/eval.c  2020-11-04 20:19:02.369688601 +0100
***************
*** 3266,3272 ****
       * Lambda: {arg, arg -> expr}
       * Dictionary: {'key': val, 'key': val}
       */
!     case '{': ret = get_lambda_tv(arg, rettv, evalarg);
                if (ret == NOTDONE)
                    ret = eval_dict(arg, rettv, evalarg, FALSE);
                break;
--- 3266,3272 ----
       * Lambda: {arg, arg -> expr}
       * Dictionary: {'key': val, 'key': val}
       */
!     case '{': ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
                if (ret == NOTDONE)
                    ret = eval_dict(arg, rettv, evalarg, FALSE);
                break;
***************
*** 3554,3560 ****
      *arg += 2;
      rettv->v_type = VAR_UNKNOWN;
  
!     ret = get_lambda_tv(arg, rettv, evalarg);
      if (ret != OK)
        return FAIL;
      else if (**arg != '(')
--- 3554,3560 ----
      *arg += 2;
      rettv->v_type = VAR_UNKNOWN;
  
!     ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
      if (ret != OK)
        return FAIL;
      else if (**arg != '(')
*** ../vim-8.2.1955/src/testdir/test_vim9_disassemble.vim       2020-10-24 
23:08:34.711491620 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-11-04 21:42:30.000083952 
+0100
***************
*** 788,793 ****
--- 788,815 ----
          instr)
  enddef
  
+ def LambdaWithType(): number
+   var Ref = {a: number -> a + 10}
+   return Ref(g:value)
+ enddef
+ 
+ def Test_disassemble_lambda_with_type()
+   g:value = 5
+   assert_equal(15, LambdaWithType())
+   var instr = execute('disassemble LambdaWithType')
+   assert_match('LambdaWithType\_s*' ..
+         'var Ref = {a: number -> a + 10}\_s*' ..
+         '\d FUNCREF <lambda>\d\+\_s*' ..
+         '\d STORE $0\_s*' ..
+         'return Ref(g:value)\_s*' ..
+         '\d LOADG g:value\_s*' ..
+         '\d LOAD $0\_s*' ..
+         '\d CHECKTYPE number stack\[-2\]\_s*' ..
+         '\d PCALL (argc 1)\_s*' ..
+         '\d RETURN',
+         instr)
+ enddef
+ 
  def NestedOuter()
    def g:Inner()
      echomsg "inner"
*** ../vim-8.2.1955/src/testdir/test_vim9_func.vim      2020-10-30 
21:49:36.302568284 +0100
--- src/testdir/test_vim9_func.vim      2020-11-04 21:53:54.245128579 +0100
***************
*** 322,329 ****
    CheckDefFailure(['bufnr(xxx)'], 'E1001:')
    CheckScriptFailure(['def Func(Ref: func(s: string))'], 'E475:')
  
-   CheckDefFailure(['echo {i -> 0}()'], 'E119: Not enough arguments for 
function: {i -> 0}()')
- 
    var lines =<< trim END
      vim9script
      def Func(s: string)
--- 322,327 ----
***************
*** 378,383 ****
--- 376,392 ----
    delete('Xscript')
  enddef
  
+ def Test_call_lambda_args()
+   CheckDefFailure(['echo {i -> 0}()'],
+                   'E119: Not enough arguments for function: {i -> 0}()')
+ 
+   var lines =<< trim END
+       var Ref = {x: number, y: number -> x + y}
+       echo Ref(1, 'x')
+   END
+   CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected number 
but got string')
+ enddef
+ 
  " Default arg and varargs
  def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
    var res = one .. ',' .. two
*** ../vim-8.2.1955/src/version.c       2020-11-04 18:53:31.558111038 +0100
--- src/version.c       2020-11-04 21:55:06.412825623 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1956,
  /**/

-- 
Some of the well known MS-Windows errors:
        EMULTI          Multitasking attempted, system confused
        EKEYBOARD       Keyboard locked, try getting out of this one!
        EXPLAIN         Unexplained error, please tell us what happened
        EFUTURE         Reserved for our future mistakes

 /// Bram Moolenaar -- [email protected] -- 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    ///

-- 
-- 
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/202011051746.0A5HkP4Q191502%40masaka.moolenaar.net.

Raspunde prin e-mail lui