]> granicus.if.org Git - vim/commitdiff
patch 9.0.1119: type of arguments not checked when calling a partial v9.0.1119
authorBram Moolenaar <Bram@vim.org>
Sat, 31 Dec 2022 14:01:24 +0000 (14:01 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 31 Dec 2022 14:01:24 +0000 (14:01 +0000)
Problem:    Type of arguments not checked when calling a partial.
Solution:   Give an error for a wrong argument type. (closes #11753)

src/testdir/test_vim9_func.vim
src/version.c
src/vim9execute.c

index 0edf7eab713ca2837cb4438f3784e52757bf6a67..3840d1144f93cc80208496d1b689c34611db0f37 100644 (file)
@@ -4277,6 +4277,32 @@ func Test_lambda_allocation_failure()
   bw!
 endfunc
 
+def Test_lambda_argument_type_check()
+  var lines =<< trim END
+      vim9script
+
+      def Scan(ll: list<any>): func(func(any))
+        return (Emit: func(any)) => {
+          for e in ll
+            Emit(e)
+          endfor
+        }
+      enddef
+
+      def Sum(Cont: func(func(any))): any
+        var sum = 0.0
+        Cont((v: float) => {  # <== NOTE: the lambda expects a float
+          sum += v
+        })
+        return sum
+      enddef
+
+      const ml = [3.0, 2, 7]
+      echo Scan(ml)->Sum()
+  END
+  v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected float but got number')
+enddef
+
 def Test_multiple_funcref()
   # This was using a NULL pointer
   var lines =<< trim END
index a66b58893ab8baa4abfc75be224d8eafccf567f0..92c70ea99e7178b85d54fa01b8ed46f80a7ae4e6 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1119,
 /**/
     1118,
 /**/
index 06229b884a35e91f5967d38b83095d27dc2ddef6..2bc81c38b18ef224e7e726f6e0ac60a7d161aeac 100644 (file)
@@ -376,6 +376,41 @@ get_pt_outer(partial_T *pt)
     return &ptref->pt_outer;
 }
 
+/*
+ * Check "argcount" arguments on the stack against what "ufunc" expects.
+ * "off" is the offset of arguments on the stack.
+ * Return OK or FAIL.
+ */
+    static int
+check_ufunc_arg_types(ufunc_T *ufunc, int argcount, int off, ectx_T *ectx)
+{
+    if (ufunc->uf_arg_types != NULL || ufunc->uf_va_type != NULL)
+    {
+       typval_T        *argv = STACK_TV_BOT(0) - argcount - off;
+
+       // The function can change at runtime, check that the argument
+       // types are correct.
+       for (int i = 0; i < argcount; ++i)
+       {
+           type_T *type = NULL;
+
+           // assume a v:none argument, using the default value, is always OK
+           if (argv[i].v_type == VAR_SPECIAL
+                                        && argv[i].vval.v_number == VVAL_NONE)
+               continue;
+
+           if (i < ufunc->uf_args.ga_len && ufunc->uf_arg_types != NULL)
+               type = ufunc->uf_arg_types[i];
+           else if (ufunc->uf_va_type != NULL)
+               type = ufunc->uf_va_type->tt_member;
+           if (type != NULL && check_typval_arg_type(type,
+                                           &argv[i], NULL, i + 1) == FAIL)
+               return FAIL;
+       }
+    }
+    return OK;
+}
+
 /*
  * Call compiled function "cdf_idx" from compiled code.
  * This adds a stack frame and sets the instruction pointer to the start of the
@@ -498,6 +533,10 @@ call_dfunc(
        return FAIL;
     }
 
+    // Check the argument types.
+    if (check_ufunc_arg_types(ufunc, argcount, vararg_count, ectx) == FAIL)
+       return FAIL;
+
     // Reserve space for:
     // - missing arguments
     // - stack frame
@@ -1345,26 +1384,8 @@ call_by_name(
 
     if (ufunc != NULL)
     {
-       if (ufunc->uf_arg_types != NULL || ufunc->uf_va_type != NULL)
-       {
-           int i;
-           typval_T    *argv = STACK_TV_BOT(0) - argcount;
-
-           // The function can change at runtime, check that the argument
-           // types are correct.
-           for (i = 0; i < argcount; ++i)
-           {
-               type_T *type = NULL;
-
-               if (i < ufunc->uf_args.ga_len && ufunc->uf_arg_types != NULL)
-                   type = ufunc->uf_arg_types[i];
-               else if (ufunc->uf_va_type != NULL)
-                   type = ufunc->uf_va_type->tt_member;
-               if (type != NULL && check_typval_arg_type(type,
-                                               &argv[i], NULL, i + 1) == FAIL)
-                   return FAIL;
-           }
-       }
+       if (check_ufunc_arg_types(ufunc, argcount, 0, ectx) == FAIL)
+           return FAIL;
 
        return call_ufunc(ufunc, NULL, argcount, ectx, iptr, selfdict);
     }