# HG changeset patch # User Bram Moolenaar # Date 1672347604 -3600 # Node ID 67d9fbe516a3a94f704a009ad5da473a74d2bf25 # Parent aadc3446d666a07062a7f29ceee4c72112d28306 patch 9.0.1108: type error when using "any" type and adding to float Commit: https://github.com/vim/vim/commit/c6951a76a58663ef8a773d340f2260da7455643c Author: Bram Moolenaar Date: Thu Dec 29 20:56:24 2022 +0000 patch 9.0.1108: type error when using "any" type and adding to float Problem: Type error when using "any" type and adding a number to a float. Solution: Accept both a number and a float. (closes https://github.com/vim/vim/issues/11753) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -215,7 +215,7 @@ check_arg_type( type_T *actual, argcontext_T *context) { - return need_type(actual, expected, + return need_type(actual, expected, FALSE, context->arg_idx - context->arg_count, context->arg_idx + 1, context->arg_cctx, FALSE, FALSE); } @@ -229,7 +229,7 @@ check_arg_type_mod( type_T *actual, argcontext_T *context) { - if (need_type(actual, expected, + if (need_type(actual, expected, FALSE, context->arg_idx - context->arg_count, context->arg_idx + 1, context->arg_cctx, FALSE, FALSE) == FAIL) return FAIL; diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -5,9 +5,9 @@ void update_script_var_block_id(char_u * int script_is_vim9(void); int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack); int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg); -int need_type_where(type_T *actual, type_T *expected, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const); -int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); -lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); +int need_type_where(type_T *actual, type_T *expected, int number_ok, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const); +int need_type(type_T *actual, type_T *expected, int number_ok, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); +lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int assign, type_T *type); int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx, cstack_T *cstack); imported_T *find_imported(char_u *name, size_t len, int load); char_u *may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -15,7 +15,7 @@ int generate_COMPARE(cctx_T *cctx, exprt int generate_CONCAT(cctx_T *cctx, int count); int generate_2BOOL(cctx_T *cctx, int invert, int offset); int generate_COND2BOOL(cctx_T *cctx); -int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int offset, int is_var, int argidx); +int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int number_ok, int offset, int is_var, int argidx); int generate_SETTYPE(cctx_T *cctx, type_T *expected); int generate_tv_PUSH(cctx_T *cctx, typval_T *tv); int generate_PUSHNR(cctx_T *cctx, varnumber_T number); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1460,6 +1460,12 @@ typedef struct { type_T *type_decl; // declared type or equal to type_current } type2_T; +#define TTFLAG_VARARGS 0x01 // func args ends with "..." +#define TTFLAG_BOOL_OK 0x02 // can be converted to bool +#define TTFLAG_NUMBER_OK 0x04 // tt_type is VAR_FLOAT, VAR_NUMBER is OK +#define TTFLAG_STATIC 0x08 // one of the static types, e.g. t_any +#define TTFLAG_CONST 0x10 // cannot be changed + typedef enum { ACCESS_PRIVATE, // read/write only inside th class ACCESS_READ, // read everywhere, write only inside th class @@ -1517,11 +1523,6 @@ struct object_S int obj_copyID; // used by garbage collection }; -#define TTFLAG_VARARGS 0x01 // func args ends with "..." -#define TTFLAG_BOOL_OK 0x02 // can be converted to bool -#define TTFLAG_STATIC 0x04 // one of the static types, e.g. t_any -#define TTFLAG_CONST 0x08 // cannot be changed - /* * Structure to hold an internal variable without a name. */ diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2033,6 +2033,34 @@ def Test_expr8() v9.CheckScriptFailure(['vim9script', "var x = "], 'E15:', 2) v9.CheckDefAndScriptFailure(["var x = 123"], 'E1068:', 1) v9.CheckDefAndScriptFailure(["var x = v + return sum + enddef + + const kk = 1 + Sum(kk) + END + v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected float but got number') enddef " test low level expression diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1108, +/**/ 1107, /**/ 1106, diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -1045,7 +1045,7 @@ compile_for(char_u *arg_start, cctx_T *c if (lhs_type == &t_any) lhs_type = item_type; else if (item_type != &t_unknown - && need_type_where(item_type, lhs_type, -1, + && need_type_where(item_type, lhs_type, FALSE, -1, where, cctx, FALSE, FALSE) == FAIL) goto failed; var_lvar = reserve_local(cctx, arg, varlen, ASSIGN_FINAL, @@ -2469,7 +2469,7 @@ compile_redir(char_u *line, exarg_T *eap if (compile_assign_lhs(arg, lhs, CMD_redir, FALSE, FALSE, FALSE, 1, cctx) == FAIL) return NULL; - if (need_type(&t_string, lhs->lhs_member_type, + if (need_type(&t_string, lhs->lhs_member_type, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) return NULL; if (cctx->ctx_skip == SKIP_YES) @@ -2551,7 +2551,7 @@ compile_return(char_u *arg, int check_re int save_flags = cmdmod.cmod_flags; generate_LEGACY_EVAL(cctx, p); - if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, -1, + if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) return NULL; cmdmod.cmod_flags |= CMOD_LEGACY; @@ -2580,8 +2580,8 @@ compile_return(char_u *arg, int check_re } else { - if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, - 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, FALSE, + -1, 0, cctx, FALSE, FALSE) == FAIL) return NULL; } } diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -451,6 +451,7 @@ use_typecheck(type_T *actual, type_T *ex need_type_where( type_T *actual, type_T *expected, + int number_ok, // expect VAR_FLOAT but VAR_NUMBER is OK int offset, where_T where, cctx_T *cctx, @@ -480,7 +481,7 @@ need_type_where( // If the actual type can be the expected type add a runtime check. if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected)) { - generate_TYPECHECK(cctx, expected, offset, + generate_TYPECHECK(cctx, expected, number_ok, offset, where.wt_variable, where.wt_index); return OK; } @@ -494,6 +495,7 @@ need_type_where( need_type( type_T *actual, type_T *expected, + int number_ok, // when expected is float number is also OK int offset, int arg_idx, cctx_T *cctx, @@ -503,7 +505,7 @@ need_type( where_T where = WHERE_INIT; where.wt_index = arg_idx; - return need_type_where(actual, expected, offset, where, + return need_type_where(actual, expected, number_ok, offset, where, cctx, silent, actual_is_const); } @@ -2000,8 +2002,8 @@ compile_load_lhs( // now we can properly check the type if (rhs_type != NULL && 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) + && need_type(rhs_type, lhs->lhs_type->tt_member, FALSE, + -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } else @@ -2090,13 +2092,13 @@ compile_assign_unlet( if (range) { type = get_type_on_stack(cctx, 1); - if (need_type(type, &t_number, + if (need_type(type, &t_number, FALSE, -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } type = get_type_on_stack(cctx, 0); if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL) - && need_type(type, &t_number, + && need_type(type, &t_number, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } @@ -2357,7 +2359,7 @@ compile_assignment( emsg(_(e_cannot_use_void_value)); goto theend; } - if (need_type(stacktype, &t_list_any, -1, 0, cctx, + if (need_type(stacktype, &t_list_any, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; // If a constant list was used we can check the length right here. @@ -2424,7 +2426,7 @@ compile_assignment( { SOURCING_LNUM = start_lnum; if (lhs.lhs_has_type - && need_type(&t_list_string, lhs.lhs_type, + && need_type(&t_list_string, lhs.lhs_type, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; } @@ -2549,8 +2551,8 @@ compile_assignment( && !has_list_index(var_start + lhs.lhs_varlen, cctx)) use_type = lhs.lhs_member_type; - if (need_type_where(rhs_type, use_type, -1, where, - cctx, FALSE, is_const) == FAIL) + if (need_type_where(rhs_type, use_type, FALSE, -1, + where, cctx, FALSE, is_const) == FAIL) goto theend; } } @@ -2565,7 +2567,7 @@ compile_assignment( || lhs_type == &t_float) && rhs_type->tt_type == VAR_NUMBER) lhs_type = &t_number; - if (*p != '=' && need_type(rhs_type, lhs_type, + if (*p != '=' && need_type(rhs_type, lhs_type, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; } @@ -2622,8 +2624,8 @@ compile_assignment( if ( // If variable is float operation with number is OK. !(expected == &t_float && (stacktype == &t_number - || stacktype == &t_number_bool)) && - need_type(stacktype, expected, -1, 0, cctx, + || stacktype == &t_number_bool)) + && need_type(stacktype, expected, TRUE, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; } @@ -3104,7 +3106,7 @@ compile_def_function( ufunc->uf_arg_types[arg_idx] = val_type; } else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx], - -1, where, &cctx, FALSE, FALSE) == FAIL) + FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL) goto erret; if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL) diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -6872,16 +6872,23 @@ list_instructions(char *pfx, isn_T *inst case ISN_CHECKTYPE: { checktype_T *ct = &iptr->isn_arg.type; - char *tofree; + char *tofree = NULL; + char *typename; + + if (ct->ct_type->tt_type == VAR_FLOAT + && (ct->ct_type->tt_flags & TTFLAG_NUMBER_OK)) + typename = "float|number"; + else + typename = type_name(ct->ct_type, &tofree); if (ct->ct_arg_idx == 0) smsg("%s%4d CHECKTYPE %s stack[%d]", pfx, current, - type_name(ct->ct_type, &tofree), + typename, (int)ct->ct_off); else smsg("%s%4d CHECKTYPE %s stack[%d] %s %d", pfx, current, - type_name(ct->ct_type, &tofree), + typename, (int)ct->ct_off, ct->ct_is_var ? "var": "arg", (int)ct->ct_arg_idx); diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -99,13 +99,14 @@ compile_member(int is_slice, int *keepin vartype = VAR_DICT; if (vartype == VAR_STRING || vartype == VAR_LIST || vartype == VAR_BLOB) { - if (need_type(idxtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(idxtype, &t_number, FALSE, + -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (is_slice) { idxtype = get_type_on_stack(cctx, 1); - if (need_type(idxtype, &t_number, -2, 0, cctx, - FALSE, FALSE) == FAIL) + if (need_type(idxtype, &t_number, FALSE, + -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } } @@ -135,8 +136,8 @@ compile_member(int is_slice, int *keepin } else { - if (need_type(typep->type_curr, &t_dict_any, -2, 0, cctx, - FALSE, FALSE) == FAIL) + if (need_type(typep->type_curr, &t_dict_any, FALSE, + -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; typep->type_curr = &t_any; typep->type_decl = &t_any; @@ -1725,7 +1726,7 @@ bool_on_stack(cctx_T *cctx) // This requires a runtime type check. return generate_COND2BOOL(cctx); - return need_type(type, &t_bool, -1, 0, cctx, FALSE, FALSE); + return need_type(type, &t_bool, FALSE, -1, 0, cctx, FALSE, FALSE); } /* @@ -1759,7 +1760,7 @@ compile_leader(cctx_T *cctx, int numeric { type_T *type = get_type_on_stack(cctx, 0); if (type->tt_type != VAR_FLOAT && need_type(type, &t_number, - -1, 0, cctx, FALSE, FALSE) == FAIL) + FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; // only '-' has an effect, for '+' we only check the type @@ -2517,8 +2518,8 @@ compile_expr8(char_u **arg, cctx_T *cctx actual = get_type_on_stack(cctx, 0); if (check_type_maybe(want_type, actual, FALSE, where) != OK) { - if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) - == FAIL) + if (need_type(actual, want_type, FALSE, + -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } } @@ -2759,7 +2760,7 @@ compile_expr5(char_u **arg, cctx_T *cctx { type_T *t = get_type_on_stack(cctx, 0); - if (need_type(t, &t_number, 0, 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(t, &t_number, FALSE, 0, 0, cctx, FALSE, FALSE) == FAIL) { emsg(_(e_bitshift_ops_must_be_number)); return FAIL; @@ -2814,8 +2815,8 @@ compile_expr5(char_u **arg, cctx_T *cctx } else { - if (need_type(get_type_on_stack(cctx, 0), &t_number, 0, 0, cctx, - FALSE, FALSE) == FAIL) + if (need_type(get_type_on_stack(cctx, 0), &t_number, FALSE, + 0, 0, cctx, FALSE, FALSE) == FAIL) { emsg(_(e_bitshift_ops_must_be_number)); return FAIL; diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -576,6 +576,7 @@ generate_COND2BOOL(cctx_T *cctx) generate_TYPECHECK( cctx_T *cctx, type_T *expected, + int number_ok, // add TTFLAG_NUMBER_OK flag int offset, int is_var, int argidx) @@ -585,7 +586,21 @@ generate_TYPECHECK( RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL) return FAIL; - isn->isn_arg.type.ct_type = alloc_type(expected); + type_T *tt; + if (expected->tt_type == VAR_FLOAT && number_ok) + { + // always allocate, also for static types + tt = ALLOC_ONE(type_T); + if (tt != NULL) + { + *tt = *expected; + tt->tt_flags |= TTFLAG_NUMBER_OK; + } + } + else + tt = alloc_type(expected); + + isn->isn_arg.type.ct_type = tt; isn->isn_arg.type.ct_off = (int8_T)offset; isn->isn_arg.type.ct_is_var = is_var; isn->isn_arg.type.ct_arg_idx = (int8_T)argidx; @@ -1601,7 +1616,7 @@ generate_BCALL(cctx_T *cctx, int func_id if (maptype != NULL && maptype[0].type_decl->tt_member != NULL && maptype[0].type_decl->tt_member != &t_any) // Check that map() didn't change the item types. - generate_TYPECHECK(cctx, maptype[0].type_decl, -1, FALSE, 1); + generate_TYPECHECK(cctx, maptype[0].type_decl, FALSE, -1, FALSE, 1); return OK; } @@ -1625,7 +1640,7 @@ generate_LISTAPPEND(cctx_T *cctx) return FAIL; item_type = get_type_on_stack(cctx, 0); expected = list_type->tt_member; - if (need_type(item_type, expected, -1, 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(item_type, expected, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_LISTAPPEND) == NULL) @@ -1648,7 +1663,8 @@ generate_BLOBAPPEND(cctx_T *cctx) if (arg_type_modifiable(get_decl_type_on_stack(cctx, 1), 1) == FAIL) return FAIL; item_type = get_type_on_stack(cctx, 0); - if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) + if (need_type(item_type, &t_number, FALSE, + -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL) @@ -1713,8 +1729,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu expected = &t_any; else expected = ufunc->uf_va_type->tt_member; - if (need_type(actual, expected, -argcount + i, i + 1, cctx, - TRUE, FALSE) == FAIL) + if (need_type(actual, expected, FALSE, + -argcount + i, i + 1, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); return FAIL; @@ -1821,8 +1837,8 @@ check_func_args_from_type( expected = &t_any; else expected = type->tt_args[i]; - if (need_type(actual, expected, offset, i + 1, - cctx, TRUE, FALSE) == FAIL) + if (need_type(actual, expected, FALSE, + offset, i + 1, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); return FAIL; diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -813,6 +813,11 @@ check_type_maybe( && (actual->tt_flags & TTFLAG_BOOL_OK)) // Using number 0 or 1 for bool is OK. return OK; + if (expected->tt_type == VAR_FLOAT + && (expected->tt_flags & TTFLAG_NUMBER_OK) + && actual->tt_type == VAR_NUMBER) + // Using number where float is expected is OK here. + return OK; if (give_msg) type_mismatch_where(expected, actual, where); return FAIL; @@ -848,7 +853,8 @@ check_type_maybe( { int i; - for (i = 0; i < expected->tt_argcount && i < actual->tt_argcount; ++i) + for (i = 0; i < expected->tt_argcount + && i < actual->tt_argcount; ++i) // Allow for using "any" argument type, lambda's have them. if (actual->tt_args[i] != &t_any && check_type( expected->tt_args[i], actual->tt_args[i], FALSE,