To: vim_dev@googlegroups.com Subject: Patch 8.2.1272 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1272 Problem: Vim9: type not checked if declaration also assigns value. Solution: Check the type. (issue #6507) Files: src/eval.c, src/vim9compile.c, src/proto/vim9compile.pro, src/vim9script.c, src/vim9execute.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1271/src/eval.c 2020-07-21 21:30:55.011536496 +0200 --- src/eval.c 2020-07-22 21:40:33.498575556 +0200 *************** *** 1270,1276 **** --- 1270,1281 ---- } } else + { + if (lp->ll_type != NULL + && check_typval_type(lp->ll_type, rettv) == FAIL) + return; set_var_const(lp->ll_name, lp->ll_type, rettv, copy, flags); + } *endp = cc; } else if (var_check_lock(lp->ll_newkey == NULL *** ../vim-8.2.1271/src/vim9compile.c 2020-07-22 20:16:08.071900823 +0200 --- src/vim9compile.c 2020-07-22 21:40:36.902564024 +0200 *************** *** 478,499 **** } /* ! * Return the type_T for a typval. Only for primitive types. */ type_T * ! typval2type(typval_T *tv) { if (tv->v_type == VAR_NUMBER) return &t_number; if (tv->v_type == VAR_BOOL) return &t_bool; // not used if (tv->v_type == VAR_STRING) return &t_string; if (tv->v_type == VAR_LIST) // e.g. for v:oldfiles return &t_list_string; if (tv->v_type == VAR_DICT) // e.g. for v:completed_item return &t_dict_any; ! return &t_any; // not used } static void --- 478,583 ---- } /* ! * Get a type_T for a typval_T. ! * "type_list" is used to temporarily create types in. */ type_T * ! typval2type(typval_T *tv, garray_T *type_gap) { + type_T *actual; + type_T *member_type; + if (tv->v_type == VAR_NUMBER) return &t_number; if (tv->v_type == VAR_BOOL) return &t_bool; // not used if (tv->v_type == VAR_STRING) return &t_string; + + if (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && tv->vval.v_list->lv_first != NULL) + { + // Use the type of the first member, it is the most specific. + member_type = typval2type(&tv->vval.v_list->lv_first->li_tv, type_gap); + return get_list_type(member_type, type_gap); + } + + if (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && tv->vval.v_dict->dv_hashtab.ht_used > 0) + { + dict_iterator_T iter; + typval_T *value; + + // Use the type of the first value, it is the most specific. + dict_iterate_start(tv, &iter); + dict_iterate_next(&iter, &value); + member_type = typval2type(value, type_gap); + return get_dict_type(member_type, type_gap); + } + + if (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) + { + char_u *name = NULL; + ufunc_T *ufunc = NULL; + + if (tv->v_type == VAR_PARTIAL) + { + if (tv->vval.v_partial->pt_func != NULL) + ufunc = tv->vval.v_partial->pt_func; + else + name = tv->vval.v_partial->pt_name; + } + else + name = tv->vval.v_string; + if (name != NULL) + // TODO: how about a builtin function? + ufunc = find_func(name, FALSE, NULL); + if (ufunc != NULL && ufunc->uf_func_type != NULL) + return ufunc->uf_func_type; + } + + actual = alloc_type(type_gap); + if (actual == NULL) + return NULL; + actual->tt_type = tv->v_type; + actual->tt_member = &t_any; + + return actual; + } + + /* + * Get a type_T for a typval_T, used for v: variables. + * "type_list" is used to temporarily create types in. + */ + type_T * + typval2type_vimvar(typval_T *tv, garray_T *type_gap) + { if (tv->v_type == VAR_LIST) // e.g. for v:oldfiles return &t_list_string; if (tv->v_type == VAR_DICT) // e.g. for v:completed_item return &t_dict_any; ! return typval2type(tv, type_gap); ! } ! ! ! /* ! * Return FAIL if "expected" and "actual" don't match. ! */ ! int ! check_typval_type(type_T *expected, typval_T *actual_tv) ! { ! garray_T type_list; ! type_T *actual_type; ! int res = FAIL; ! ! ga_init2(&type_list, sizeof(type_T *), 10); ! actual_type = typval2type(actual_tv, &type_list); ! if (actual_type != NULL) ! res = check_type(expected, actual_type, TRUE); ! clear_type_list(&type_list); ! return res; } static void *************** *** 561,631 **** return ret; } - /* - * Return FAIl if "expected" and "actual" don't match. - * TODO: better type comparison - */ - int - check_argtype(type_T *expected, typval_T *actual_tv) - { - type_T actual; - type_T member; - - // TODO: should should be done with more levels - CLEAR_FIELD(actual); - actual.tt_type = actual_tv->v_type; - if (actual_tv->v_type == VAR_LIST - && actual_tv->vval.v_list != NULL - && actual_tv->vval.v_list->lv_first != NULL) - { - // Use the type of the first member, it is the most specific. - CLEAR_FIELD(member); - member.tt_type = actual_tv->vval.v_list->lv_first->li_tv.v_type; - member.tt_member = &t_any; - actual.tt_member = &member; - } - else if (actual_tv->v_type == VAR_DICT - && actual_tv->vval.v_dict != NULL - && actual_tv->vval.v_dict->dv_hashtab.ht_used > 0) - { - dict_iterator_T iter; - typval_T *value; - - // Use the type of the first value, it is the most specific. - dict_iterate_start(actual_tv, &iter); - dict_iterate_next(&iter, &value); - CLEAR_FIELD(member); - member.tt_type = value->v_type; - member.tt_member = &t_any; - actual.tt_member = &member; - } - else if (actual_tv->v_type == VAR_FUNC || actual_tv->v_type == VAR_PARTIAL) - { - char_u *name = NULL; - ufunc_T *ufunc = NULL; - - if (actual_tv->v_type == VAR_PARTIAL) - { - if (actual_tv->vval.v_partial->pt_func != NULL) - ufunc = actual_tv->vval.v_partial->pt_func; - else - name = actual_tv->vval.v_partial->pt_name; - } - else - name = actual_tv->vval.v_string; - if (name != NULL) - // TODO: how about a builtin function? - ufunc = find_func(name, FALSE, NULL); - if (ufunc != NULL && ufunc->uf_func_type != NULL) - actual = *ufunc->uf_func_type; - else - actual.tt_member = &t_any; - } - else - actual.tt_member = &t_any; - return check_type(expected, &actual, TRUE); - } - ///////////////////////////////////////////////////////////////////// // Following generate_ functions expect the caller to call ga_grow(). --- 645,650 ---- *************** *** 1314,1320 **** semsg(_(e_var_notfound), name); return FAIL; } ! type = typval2type(get_vim_var_tv(vidx)); return generate_LOAD(cctx, ISN_LOADV, vidx, NULL, type); } --- 1333,1339 ---- semsg(_(e_var_notfound), name); return FAIL; } ! type = typval2type_vimvar(get_vim_var_tv(vidx), cctx->ctx_type_list); return generate_LOAD(cctx, ISN_LOADV, vidx, NULL, type); } *************** *** 5235,5241 **** goto theend; dest = dest_vimvar; vtv = get_vim_var_tv(vimvaridx); ! type = typval2type(vtv); if (is_decl) { vim9_declare_error(name); --- 5254,5260 ---- goto theend; dest = dest_vimvar; vtv = get_vim_var_tv(vimvaridx); ! type = typval2type_vimvar(vtv, cctx->ctx_type_list); if (is_decl) { vim9_declare_error(name); *** ../vim-8.2.1271/src/proto/vim9compile.pro 2020-07-19 14:41:54.625623029 +0200 --- src/proto/vim9compile.pro 2020-07-22 21:40:46.066532986 +0200 *************** *** 1,9 **** /* vim9compile.c */ int check_defined(char_u *p, size_t len, cctx_T *cctx); void clear_type_list(garray_T *gap); ! type_T *typval2type(typval_T *tv); int check_type(type_T *expected, type_T *actual, int give_msg); - int check_argtype(type_T *expected, typval_T *actual_tv); int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2); char_u *skip_type(char_u *start); type_T *parse_type(char_u **arg, garray_T *type_gap); --- 1,10 ---- /* vim9compile.c */ int check_defined(char_u *p, size_t len, cctx_T *cctx); void clear_type_list(garray_T *gap); ! type_T *typval2type(typval_T *tv, garray_T *type_gap); ! type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap); ! int check_typval_type(type_T *expected, typval_T *actual_tv); int check_type(type_T *expected, type_T *actual, int give_msg); int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2); char_u *skip_type(char_u *start); type_T *parse_type(char_u **arg, garray_T *type_gap); *** ../vim-8.2.1271/src/vim9script.c 2020-07-12 17:07:01.020810937 +0200 --- src/vim9script.c 2020-07-22 21:37:19.427227115 +0200 *************** *** 557,563 **** semsg(_(e_readonlyvar), name); return FAIL; } ! return check_type(sv->sv_type, typval2type(value), TRUE); } } iemsg("check_script_var_type(): not found"); --- 557,563 ---- semsg(_(e_readonlyvar), name); return FAIL; } ! return check_typval_type(sv->sv_type, value); } } iemsg("check_script_var_type(): not found"); *** ../vim-8.2.1271/src/vim9execute.c 2020-07-21 21:30:55.015536477 +0200 --- src/vim9execute.c 2020-07-22 21:40:41.358548945 +0200 *************** *** 737,743 **** for (idx = 0; idx < argc; ++idx) { if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len ! && check_argtype(ufunc->uf_arg_types[idx], &argv[idx]) == FAIL) goto failed_early; copy_tv(&argv[idx], STACK_TV_BOT(0)); ++ectx.ec_stack.ga_len; --- 737,744 ---- for (idx = 0; idx < argc; ++idx) { if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len ! && check_typval_type(ufunc->uf_arg_types[idx], &argv[idx]) ! == FAIL) goto failed_early; copy_tv(&argv[idx], STACK_TV_BOT(0)); ++ectx.ec_stack.ga_len; *** ../vim-8.2.1271/src/testdir/test_vim9_script.vim 2020-07-22 18:17:04.235863872 +0200 --- src/testdir/test_vim9_script.vim 2020-07-22 21:01:04.622022991 +0200 *************** *** 29,34 **** --- 29,37 ---- call CheckDefFailure(['let x:string = "x"'], 'E1069:') call CheckDefFailure(['let a:string = "x"'], 'E1069:') + let nr: number = 1234 + call CheckDefFailure(['let nr: number = "asdf"'], 'E1013:') + let a: number = 6 #comment assert_equal(6, a) *** ../vim-8.2.1271/src/version.c 2020-07-22 20:16:08.071900823 +0200 --- src/version.c 2020-07-22 21:01:21.653960525 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1272, /**/ -- hundred-and-one symptoms of being an internet addict: 59. Your wife says communication is important in a marriage...so you buy another computer and install a second phone line so the two of you can chat. /// 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 ///