To: vim_dev@googlegroups.com Subject: Patch 8.2.2362 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2362 Problem: Vim9: check of builtin function argument type is incomplete. Solution: Use need_type() instead of check_arg_type(). Files: src/vim9compile.c, src/proto/vim9compile.pro, src/evalfunc.c, src/proto/evalfunc.pro, src/vim9type.c, src/proto/vim9type.pro, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.2361/src/vim9compile.c 2021-01-14 20:35:46.254002151 +0100 --- src/vim9compile.c 2021-01-16 15:56:19.864363365 +0100 *************** *** 878,888 **** * If "actual_is_const" is TRUE then the type won't change at runtime, do not * generate a TYPECHECK. */ ! static int need_type( type_T *actual, type_T *expected, int offset, cctx_T *cctx, int silent, int actual_is_const) --- 878,889 ---- * If "actual_is_const" is TRUE then the type won't change at runtime, do not * generate a TYPECHECK. */ ! int need_type( type_T *actual, type_T *expected, int offset, + int arg_idx, cctx_T *cctx, int silent, int actual_is_const) *************** *** 896,902 **** return OK; } ! if (check_type(expected, actual, FALSE, 0) == OK) return OK; // If the actual type can be the expected type add a runtime check. --- 897,903 ---- return 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,914 **** } if (!silent) ! type_mismatch(expected, actual); return FAIL; } --- 909,915 ---- } if (!silent) ! arg_type_mismatch(expected, actual, arg_idx); return FAIL; } *************** *** 931,937 **** // This requires a runtime type check. return generate_COND2BOOL(cctx); ! return need_type(type, &t_bool, -1, cctx, FALSE, FALSE); } /* --- 932,938 ---- // This requires a runtime type check. return generate_COND2BOOL(cctx); ! return need_type(type, &t_bool, -1, 0, cctx, FALSE, FALSE); } /* *************** *** 1613,1619 **** { // 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) return FAIL; if (internal_func_is_map(func_idx)) maptype = *argtypes; --- 1614,1621 ---- { // 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, ! cctx) == FAIL) return FAIL; if (internal_func_is_map(func_idx)) maptype = *argtypes; *************** *** 1656,1662 **** 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) return FAIL; if (generate_instr(cctx, ISN_LISTAPPEND) == NULL) --- 1658,1664 ---- 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, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_LISTAPPEND) == NULL) *************** *** 1678,1684 **** // 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) return FAIL; if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL) --- 1680,1686 ---- // 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, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL) *************** *** 1733,1739 **** 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, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); --- 1735,1741 ---- 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, 0, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); *************** *** 1850,1856 **** type->tt_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); --- 1852,1858 ---- type->tt_argcount - 1]->tt_member; else expected = type->tt_args[i]; ! if (need_type(actual, expected, offset, 0, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); *************** *** 3135,3141 **** { type_T *keytype = ((type_T **)stack->ga_data) [stack->ga_len - 1]; ! if (need_type(keytype, &t_string, -1, cctx, FALSE, FALSE) == FAIL) return FAIL; } --- 3137,3143 ---- { type_T *keytype = ((type_T **)stack->ga_data) [stack->ga_len - 1]; ! if (need_type(keytype, &t_string, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } *************** *** 3808,3820 **** vtype = VAR_DICT; if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) { ! if (need_type(valtype, &t_number, -1, 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, FALSE, FALSE) == FAIL) return FAIL; } --- 3810,3822 ---- vtype = VAR_DICT; if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) { ! 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, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } *************** *** 3836,3842 **** } else { ! if (need_type(*typep, &t_dict_any, -2, cctx, FALSE, FALSE) == FAIL) return FAIL; *typep = &t_any; --- 3838,3844 ---- } else { ! if (need_type(*typep, &t_dict_any, -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; *typep = &t_any; *************** *** 4235,4241 **** 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) return FAIL; } } --- 4237,4243 ---- 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, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } } *************** *** 4917,4923 **** return NULL; } if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, ! cctx, FALSE, FALSE) == FAIL) return NULL; } } --- 4919,4925 ---- return NULL; } if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, ! 0, cctx, FALSE, FALSE) == FAIL) return NULL; } } *************** *** 5831,5837 **** : ((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, FALSE, FALSE) == FAIL) return FAIL; } --- 5833,5839 ---- : ((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, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } *************** *** 5976,5982 **** emsg(_(e_cannot_use_void_value)); goto theend; } ! if (need_type(stacktype, &t_list_any, -1, cctx, FALSE, FALSE) == FAIL) goto theend; // TODO: check the length of a constant list here --- 5978,5984 ---- emsg(_(e_cannot_use_void_value)); goto theend; } ! 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,6135 **** // 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, FALSE, is_const) == FAIL) goto theend; } } else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type, ! -1, cctx, FALSE, FALSE) == FAIL) goto theend; } else if (cmdidx == CMD_final) --- 6125,6137 ---- // 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, 0, cctx, FALSE, is_const) == FAIL) goto theend; } } else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type, ! -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; } else if (cmdidx == CMD_final) *************** *** 6216,6222 **** // If variable is float operation with number is OK. !(expected == &t_float && stacktype == &t_number) && #endif ! need_type(stacktype, expected, -1, cctx, FALSE, FALSE) == FAIL) goto theend; --- 6218,6224 ---- // If variable is float operation with number is OK. !(expected == &t_float && stacktype == &t_number) && #endif ! need_type(stacktype, expected, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; *************** *** 6925,6931 **** // 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) { drop_scope(cctx); return NULL; --- 6927,6933 ---- // 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, 0, cctx, FALSE, FALSE) == FAIL) { drop_scope(cctx); return NULL; *** ../vim-8.2.2361/src/proto/vim9compile.pro 2021-01-09 15:45:20.353451132 +0100 --- src/proto/vim9compile.pro 2021-01-16 15:41:13.938984124 +0100 *************** *** 2,7 **** --- 2,8 ---- 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); *** ../vim-8.2.2361/src/evalfunc.c 2021-01-13 21:46:53.832589880 +0100 --- src/evalfunc.c 2021-01-16 15:59:15.951901386 +0100 *************** *** 275,280 **** --- 275,281 ---- 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 *************** *** 283,288 **** --- 284,305 ---- 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. */ static int *************** *** 301,307 **** static int arg_number(type_T *type, argcontext_T *context) { ! return check_arg_type(&t_number, type, context->arg_idx + 1); } /* --- 318,324 ---- static int arg_number(type_T *type, argcontext_T *context) { ! return check_arg_type(&t_number, type, context); } /* *************** *** 310,316 **** static int arg_string(type_T *type, argcontext_T *context) { ! return check_arg_type(&t_string, type, context->arg_idx + 1); } /* --- 327,333 ---- static int arg_string(type_T *type, argcontext_T *context) { ! return check_arg_type(&t_string, type, context); } /* *************** *** 348,354 **** { type_T *prev_type = context->arg_types[context->arg_idx - 1]; ! return check_arg_type(prev_type, type, context->arg_idx + 1); } /* --- 365,371 ---- { type_T *prev_type = context->arg_types[context->arg_idx - 1]; ! return check_arg_type(prev_type, type, context); } /* *************** *** 362,368 **** 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 OK; } --- 379,385 ---- 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); return OK; } *************** *** 384,390 **** // probably VAR_ANY, can't check return OK; ! return check_arg_type(expected, type, context->arg_idx + 1); } /* --- 401,407 ---- // probably VAR_ANY, can't check return OK; ! return check_arg_type(expected, type, context); } /* *************** *** 1931,1937 **** * 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) { argcheck_T *argchecks = global_functions[idx].f_argcheck; int i; --- 1948,1958 ---- * 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, ! cctx_T *cctx) { argcheck_T *argchecks = global_functions[idx].f_argcheck; int i; *************** *** 1942,1947 **** --- 1963,1969 ---- context.arg_count = argcount; context.arg_types = types; + context.arg_cctx = cctx; for (i = 0; i < argcount; ++i) if (argchecks[i] != NULL) { *** ../vim-8.2.2361/src/proto/evalfunc.pro 2021-01-10 22:42:46.916847071 +0100 --- src/proto/evalfunc.pro 2021-01-16 15:48:51.973585991 +0100 *************** *** 4,10 **** 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); 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); --- 4,10 ---- 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, 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); *** ../vim-8.2.2361/src/vim9type.c 2021-01-12 21:48:55.879131998 +0100 --- src/vim9type.c 2021-01-16 15:55:18.936525110 +0100 *************** *** 514,538 **** } /* - * 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) { int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0; int i; --- 514,528 ---- } /* * 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) { int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0; int i; *** ../vim-8.2.2361/src/proto/vim9type.pro 2021-01-12 21:48:55.879131998 +0100 --- src/proto/vim9type.pro 2021-01-16 15:48:56.185574013 +0100 *************** *** 15,21 **** 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); --- 15,20 ---- *** ../vim-8.2.2361/src/testdir/test_vim9_builtin.vim 2021-01-13 21:46:53.832589880 +0100 --- src/testdir/test_vim9_builtin.vim 2021-01-16 16:04:27.403098215 +0100 *************** *** 241,246 **** --- 241,249 ---- CheckDefFailure(['extend({a: 1}, 42)'], 'E1013: Argument 2: type mismatch, expected dict but got number') CheckDefFailure(['extend({a: 1}, {b: "x"})'], 'E1013: Argument 2: type mismatch, expected dict but got dict') 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 but got list') + CheckDefExecFailure(['extend([1], ["b", 1])'], 'E1012: Type mismatch; expected list but got list') enddef def Test_extendnew() *** ../vim-8.2.2361/src/version.c 2021-01-16 14:34:42.219758927 +0100 --- src/version.c 2021-01-16 15:34:23.844532727 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2362, /**/ -- hundred-and-one symptoms of being an internet addict: 164. You got out to buy software, instead of going out for a beer. /// Bram Moolenaar -- Bram@Moolenaar.net -- 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 ///