]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.2362: Vim9: check of builtin function argument type is incomplete v8.2.2362
authorBram Moolenaar <Bram@vim.org>
Sat, 16 Jan 2021 15:07:01 +0000 (16:07 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 16 Jan 2021 15:07:01 +0000 (16:07 +0100)
Problem:    Vim9: check of builtin function argument type is incomplete.
Solution:   Use need_type() instead of check_arg_type().

src/evalfunc.c
src/proto/evalfunc.pro
src/proto/vim9compile.pro
src/proto/vim9type.pro
src/testdir/test_vim9_builtin.vim
src/version.c
src/vim9compile.c
src/vim9type.c

index 0e800c2b6f5aae77db3f5b308c8caf0f8068458c..61cca01f8a5c951885b6bde9f45de63a43816515 100644 (file)
@@ -275,6 +275,7 @@ typedef struct {
     int                arg_count;      // actual argument count
     type_T     **arg_types;    // list of argument types
     int                arg_idx;        // current argument index (first arg is zero)
+    cctx_T     *arg_cctx;
 } argcontext_T;
 
 // A function to check one argument type.  The first argument is the type to
@@ -282,6 +283,22 @@ typedef struct {
 // E.g. if "arg_idx" is 1, then (type - 1) is the first argument type.
 typedef int (*argcheck_T)(type_T *, argcontext_T *);
 
+/*
+ * Call need_type() to check an argument type.
+ */
+    static int
+check_arg_type(
+       type_T          *expected,
+       type_T          *actual,
+       argcontext_T    *context)
+{
+    // TODO: would be useful to know if "actual" is a constant and pass it to
+    // need_type() to get a compile time error if possible.
+    return need_type(actual, expected,
+           context->arg_idx - context->arg_count, context->arg_idx + 1,
+           context->arg_cctx, FALSE, FALSE);
+}
+
 /*
  * Check "type" is a float or a number.
  */
@@ -301,7 +318,7 @@ arg_float_or_nr(type_T *type, argcontext_T *context)
     static int
 arg_number(type_T *type, argcontext_T *context)
 {
-    return check_arg_type(&t_number, type, context->arg_idx + 1);
+    return check_arg_type(&t_number, type, context);
 }
 
 /*
@@ -310,7 +327,7 @@ arg_number(type_T *type, argcontext_T *context)
     static int
 arg_string(type_T *type, argcontext_T *context)
 {
-    return check_arg_type(&t_string, type, context->arg_idx + 1);
+    return check_arg_type(&t_string, type, context);
 }
 
 /*
@@ -348,7 +365,7 @@ arg_same_as_prev(type_T *type, argcontext_T *context)
 {
     type_T *prev_type = context->arg_types[context->arg_idx - 1];
 
-    return check_arg_type(prev_type, type, context->arg_idx + 1);
+    return check_arg_type(prev_type, type, context);
 }
 
 /*
@@ -362,7 +379,7 @@ arg_same_struct_as_prev(type_T *type, argcontext_T *context)
     type_T *prev_type = context->arg_types[context->arg_idx - 1];
 
     if (prev_type->tt_type != context->arg_types[context->arg_idx]->tt_type)
-       return check_arg_type(prev_type, type, context->arg_idx + 1);
+       return check_arg_type(prev_type, type, context);
     return OK;
 }
 
@@ -384,7 +401,7 @@ arg_item_of_prev(type_T *type, argcontext_T *context)
        // probably VAR_ANY, can't check
        return OK;
 
-    return check_arg_type(expected, type, context->arg_idx + 1);
+    return check_arg_type(expected, type, context);
 }
 
 /*
@@ -1931,7 +1948,11 @@ internal_func_name(int idx)
  * Return FAIL and gives an error message when a type is wrong.
  */
     int
-internal_func_check_arg_types(type_T **types, int idx, int argcount)
+internal_func_check_arg_types(
+       type_T  **types,
+       int     idx,
+       int     argcount,
+       cctx_T  *cctx)
 {
     argcheck_T *argchecks = global_functions[idx].f_argcheck;
     int                i;
@@ -1942,6 +1963,7 @@ internal_func_check_arg_types(type_T **types, int idx, int argcount)
 
        context.arg_count = argcount;
        context.arg_types = types;
+       context.arg_cctx = cctx;
        for (i = 0; i < argcount; ++i)
            if (argchecks[i] != NULL)
            {
index ad4b98a29d872a49b83d4edaeb7d61a0be409375..13dffacfff44a63208c372843fbb5292bf49990c 100644 (file)
@@ -4,7 +4,7 @@ char_u *get_expr_name(expand_T *xp, int idx);
 int find_internal_func(char_u *name);
 int has_internal_func(char_u *name);
 char *internal_func_name(int idx);
-int internal_func_check_arg_types(type_T **types, int idx, int argcount);
+int internal_func_check_arg_types(type_T **types, int idx, int argcount, cctx_T *cctx);
 type_T *internal_func_ret_type(int idx, int argcount, type_T **argtypes);
 int internal_func_is_map(int idx);
 int check_internal_func(int idx, int argcount);
index 7800d3ba59b2a4ec95bef94e7368f4f5a1464cd8..d1d1b0c7d0b49ffe3e59e9d5e0e2e1b12caec3fe 100644 (file)
@@ -2,6 +2,7 @@
 int check_defined(char_u *p, size_t len, cctx_T *cctx);
 int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2);
 int use_typecheck(type_T *actual, type_T *expected);
+int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);
 int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx);
 imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
 imported_T *find_imported_in_script(char_u *name, size_t len, int sid);
index 316c65011597686808e18ddf710591ce0bd021a0..f7b472c9e6895a5d283a242e1f4e600233af8318 100644 (file)
@@ -15,7 +15,6 @@ int check_typval_type(type_T *expected, typval_T *actual_tv, int argidx);
 void type_mismatch(type_T *expected, type_T *actual);
 void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
 int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
-int check_arg_type(type_T *expected, type_T *actual, int argidx);
 int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
 char_u *skip_type(char_u *start, int optional);
 type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
index f1c5de14e0ce7caff5c27eb04d1f916d43052dcf..180070e0aa3fec4a1180e357c978378e30d83017 100644 (file)
@@ -241,6 +241,9 @@ def Test_extend_arg_types()
   CheckDefFailure(['extend({a: 1}, 42)'], 'E1013: Argument 2: type mismatch, expected dict<number> but got number')
   CheckDefFailure(['extend({a: 1}, {b: "x"})'], 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string>')
   CheckDefFailure(['extend({a: 1}, {b: 2}, 1)'], 'E1013: Argument 3: type mismatch, expected string but got number')
+
+  CheckDefFailure(['extend([1], ["b"])'], 'E1013: Argument 2: type mismatch, expected list<number> but got list<string>')
+  CheckDefExecFailure(['extend([1], ["b", 1])'], 'E1012: Type mismatch; expected list<number> but got list<any>')
 enddef
 
 def Test_extendnew()
index 8dccaf280eac0c5cb0c41fa1645a2778d04fc278..73bdc24dc5ae272183f64c25232de12670b6ceac 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2362,
 /**/
     2361,
 /**/
index b0b5973dd594d4f9db94e7726f118c9a3c8d910f..fb7aeddd49abde18feb996d303ab3dec9949815b 100644 (file)
@@ -878,11 +878,12 @@ use_typecheck(type_T *actual, type_T *expected)
  * If "actual_is_const" is TRUE then the type won't change at runtime, do not
  * generate a TYPECHECK.
  */
-    static int
+    int
 need_type(
        type_T  *actual,
        type_T  *expected,
        int     offset,
+       int     arg_idx,
        cctx_T  *cctx,
        int     silent,
        int     actual_is_const)
@@ -896,7 +897,7 @@ need_type(
        return OK;
     }
 
-    if (check_type(expected, actual, FALSE, 0) == OK)
+    if (check_type(expected, actual, FALSE, arg_idx) == OK)
        return OK;
 
     // If the actual type can be the expected type add a runtime check.
@@ -908,7 +909,7 @@ need_type(
     }
 
     if (!silent)
-       type_mismatch(expected, actual);
+       arg_type_mismatch(expected, actual, arg_idx);
     return FAIL;
 }
 
@@ -931,7 +932,7 @@ bool_on_stack(cctx_T *cctx)
        // This requires a runtime type check.
        return generate_COND2BOOL(cctx);
 
-    return need_type(type, &t_bool, -1, cctx, FALSE, FALSE);
+    return need_type(type, &t_bool, -1, 0, cctx, FALSE, FALSE);
 }
 
 /*
@@ -1613,7 +1614,8 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
     {
        // Check the types of the arguments.
        argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount;
-       if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL)
+       if (internal_func_check_arg_types(argtypes, func_idx, argcount,
+                                                                cctx) == FAIL)
            return FAIL;
        if (internal_func_is_map(func_idx))
            maptype = *argtypes;
@@ -1656,7 +1658,7 @@ generate_LISTAPPEND(cctx_T *cctx)
     list_type = ((type_T **)stack->ga_data)[stack->ga_len - 2];
     item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
     expected = list_type->tt_member;
-    if (need_type(item_type, expected, -1, cctx, FALSE, FALSE) == FAIL)
+    if (need_type(item_type, expected, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
 
     if (generate_instr(cctx, ISN_LISTAPPEND) == NULL)
@@ -1678,7 +1680,7 @@ generate_BLOBAPPEND(cctx_T *cctx)
 
     // Caller already checked that blob_type is a blob.
     item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
-    if (need_type(item_type, &t_number, -1, cctx, FALSE, FALSE) == FAIL)
+    if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
 
     if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL)
@@ -1733,7 +1735,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
            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, cctx,
+           if (need_type(actual, expected, -argcount + i, 0, cctx,
                                                          TRUE, FALSE) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
@@ -1850,7 +1852,7 @@ generate_PCALL(
                                             type->tt_argcount - 1]->tt_member;
                    else
                        expected = type->tt_args[i];
-                   if (need_type(actual, expected, offset,
+                   if (need_type(actual, expected, offset, 0,
                                                    cctx, TRUE, FALSE) == FAIL)
                    {
                        arg_type_mismatch(expected, actual, i + 1);
@@ -3135,7 +3137,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            {
                type_T *keytype = ((type_T **)stack->ga_data)
                                                       [stack->ga_len - 1];
-               if (need_type(keytype, &t_string, -1, cctx,
+               if (need_type(keytype, &t_string, -1, 0, cctx,
                                                     FALSE, FALSE) == FAIL)
                    return FAIL;
            }
@@ -3808,13 +3810,13 @@ compile_subscript(
                vtype = VAR_DICT;
            if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
            {
-               if (need_type(valtype, &t_number, -1, cctx,
+               if (need_type(valtype, &t_number, -1, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
                    return FAIL;
                if (is_slice)
                {
                    valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
-                   if (need_type(valtype, &t_number, -2, cctx,
+                   if (need_type(valtype, &t_number, -2, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
                        return FAIL;
                }
@@ -3836,7 +3838,7 @@ compile_subscript(
                }
                else
                {
-                   if (need_type(*typep, &t_dict_any, -2, cctx,
+                   if (need_type(*typep, &t_dict_any, -2, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
                        return FAIL;
                    *typep = &t_any;
@@ -4235,7 +4237,7 @@ compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
        actual = ((type_T **)stack->ga_data)[stack->ga_len - 1];
        if (check_type(want_type, actual, FALSE, 0) == FAIL)
        {
-           if (need_type(actual, want_type, -1, cctx, FALSE, FALSE) == FAIL)
+           if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
        }
     }
@@ -4917,7 +4919,7 @@ compile_return(char_u *arg, int check_return_type, cctx_T *cctx)
                    return NULL;
                }
                if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1,
-                                                  cctx, FALSE, FALSE) == FAIL)
+                                               0, cctx, FALSE, FALSE) == FAIL)
                    return NULL;
            }
        }
@@ -5831,7 +5833,7 @@ compile_assign_unlet(
                  : ((type_T **)stack->ga_data)[stack->ga_len - 1];
        // now we can properly check the type
        if (lhs->lhs_type->tt_member != NULL && rhs_type != &t_void
-               && need_type(rhs_type, lhs->lhs_type->tt_member, -2, cctx,
+               && need_type(rhs_type, lhs->lhs_type->tt_member, -2, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
            return FAIL;
     }
@@ -5976,7 +5978,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                emsg(_(e_cannot_use_void_value));
                goto theend;
            }
-           if (need_type(stacktype, &t_list_any, -1, cctx,
+           if (need_type(stacktype, &t_list_any, -1, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
                goto theend;
            // TODO: check the length of a constant list here
@@ -6123,13 +6125,13 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        // without operator check type here, otherwise below
                        if (lhs.lhs_has_index)
                            use_type = lhs.lhs_member_type;
-                       if (need_type(rhs_type, use_type, -1, cctx,
+                       if (need_type(rhs_type, use_type, -1, 0, cctx,
                                                      FALSE, is_const) == FAIL)
                            goto theend;
                    }
                }
                else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type,
-                                              -1, cctx, FALSE, FALSE) == FAIL)
+                                           -1, 0, cctx, FALSE, FALSE) == FAIL)
                    goto theend;
            }
            else if (cmdidx == CMD_final)
@@ -6216,7 +6218,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                // If variable is float operation with number is OK.
                !(expected == &t_float && stacktype == &t_number) &&
 #endif
-                   need_type(stacktype, expected, -1, cctx,
+                   need_type(stacktype, expected, -1, 0, cctx,
                                                         FALSE, FALSE) == FAIL)
                goto theend;
 
@@ -6925,7 +6927,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
     // Now that we know the type of "var", check that it is a list, now or at
     // runtime.
     vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
-    if (need_type(vartype, &t_list_any, -1, cctx, FALSE, FALSE) == FAIL)
+    if (need_type(vartype, &t_list_any, -1, 0, cctx, FALSE, FALSE) == FAIL)
     {
        drop_scope(cctx);
        return NULL;
index 85f9bd8347f6afece54e2a2552c0b8705c9e8dd8..81b0db22dfb15b929e280e4a390cf033770e47b6 100644 (file)
@@ -513,26 +513,16 @@ check_type(type_T *expected, type_T *actual, int give_msg, int argidx)
     return ret;
 }
 
-/*
- * Like check_type() but also allow for a runtime type check. E.g. "any" can be
- * used for "number".
- */
-    int
-check_arg_type(type_T *expected, type_T *actual, int argidx)
-{
-    if (check_type(expected, actual, FALSE, 0) == OK
-                                           || use_typecheck(actual, expected))
-       return OK;
-    // TODO: should generate a TYPECHECK instruction.
-    return check_type(expected, actual, TRUE, argidx);
-}
-
 /*
  * Check that the arguments of "type" match "argvars[argcount]".
  * Return OK/FAIL.
  */
     int
-check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name)
+check_argument_types(
+       type_T      *type,
+       typval_T    *argvars,
+       int         argcount,
+       char_u      *name)
 {
     int            varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0;
     int            i;