To: vim_dev@googlegroups.com Subject: Patch 8.2.2759 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2759 Problem: Vim9: for loop infers type of loop variable. Solution: Do not get the member type. (closes #8102) Files: src/vim9type.c, src/proto/vim9type.pro, src/list.c, src/vim9script.c, src/proto/vim9script.pro, src/vim.h, src/testdir/test_vim9_script.vim *** ../vim-8.2.2758/src/vim9type.c 2021-03-18 22:15:00.589208304 +0100 --- src/vim9type.c 2021-04-13 20:37:54.449383083 +0200 *************** *** 252,260 **** /* * Get a type_T for a typval_T. * "type_gap" is used to temporarily create types in. */ static type_T * ! typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) { type_T *type; type_T *member_type = &t_any; --- 252,261 ---- /* * Get a type_T for a typval_T. * "type_gap" is used to temporarily create types in. + * When "do_member" is TRUE also get the member type, otherwise use "any". */ static type_T * ! typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) { type_T *type; type_T *member_type = &t_any; *************** *** 274,279 **** --- 275,282 ---- if (l == NULL || l->lv_first == NULL) return &t_list_empty; + if (!do_member) + return &t_list_any; if (l->lv_first == &range_list_item) return &t_list_number; if (l->lv_copyID == copyID) *************** *** 282,290 **** l->lv_copyID = copyID; // Use the common type of all members. ! member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap); for (li = l->lv_first->li_next; li != NULL; li = li->li_next) ! common_type(typval2type(&li->li_tv, copyID, type_gap), member_type, &member_type, type_gap); return get_list_type(member_type, type_gap); } --- 285,293 ---- l->lv_copyID = copyID; // Use the common type of all members. ! member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap, TRUE); for (li = l->lv_first->li_next; li != NULL; li = li->li_next) ! common_type(typval2type(&li->li_tv, copyID, type_gap, TRUE), member_type, &member_type, type_gap); return get_list_type(member_type, type_gap); } *************** *** 297,302 **** --- 300,307 ---- if (d == NULL || d->dv_hashtab.ht_used == 0) return &t_dict_empty; + if (!do_member) + return &t_dict_any; if (d->dv_copyID == copyID) // avoid recursion return &t_dict_any; *************** *** 305,313 **** // Use the common type of all values. dict_iterate_start(tv, &iter); dict_iterate_next(&iter, &value); ! member_type = typval2type(value, copyID, type_gap); while (dict_iterate_next(&iter, &value) != NULL) ! common_type(typval2type(value, copyID, type_gap), member_type, &member_type, type_gap); return get_dict_type(member_type, type_gap); } --- 310,318 ---- // Use the common type of all values. dict_iterate_start(tv, &iter); dict_iterate_next(&iter, &value); ! member_type = typval2type(value, copyID, type_gap, TRUE); while (dict_iterate_next(&iter, &value) != NULL) ! common_type(typval2type(value, copyID, type_gap, TRUE), member_type, &member_type, type_gap); return get_dict_type(member_type, type_gap); } *************** *** 378,388 **** /* * Get a type_T for a typval_T. * "type_list" is used to temporarily create types in. */ type_T * ! typval2type(typval_T *tv, int copyID, garray_T *type_gap) { ! type_T *type = typval2type_int(tv, copyID, type_gap); if (type != NULL && type != &t_bool && (tv->v_type == VAR_NUMBER --- 383,394 ---- /* * Get a type_T for a typval_T. * "type_list" is used to temporarily create types in. + * When "do_member" is TRUE also get the member type, otherwise use "any". */ type_T * ! typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member) { ! type_T *type = typval2type_int(tv, copyID, type_gap, do_member); if (type != NULL && type != &t_bool && (tv->v_type == VAR_NUMBER *************** *** 404,410 **** return &t_list_string; if (tv->v_type == VAR_DICT) // e.g. for v:completed_item return &t_dict_any; ! return typval2type(tv, get_copyID(), type_gap); } int --- 410,416 ---- return &t_list_string; if (tv->v_type == VAR_DICT) // e.g. for v:completed_item return &t_dict_any; ! return typval2type(tv, get_copyID(), type_gap, TRUE); } int *************** *** 429,435 **** int res = FAIL; ga_init2(&type_list, sizeof(type_T *), 10); ! actual_type = typval2type(actual_tv, get_copyID(), &type_list); if (actual_type != NULL) res = check_type(expected, actual_type, TRUE, where); clear_type_list(&type_list); --- 435,441 ---- int res = FAIL; ga_init2(&type_list, sizeof(type_T *), 10); ! actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE); if (actual_type != NULL) res = check_type(expected, actual_type, TRUE, where); clear_type_list(&type_list); *************** *** 1210,1216 **** rettv->v_type = VAR_STRING; ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list); name = type_name(type, &tofree); if (tofree != NULL) rettv->vval.v_string = (char_u *)tofree; --- 1216,1222 ---- rettv->v_type = VAR_STRING; ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list, TRUE); name = type_name(type, &tofree); if (tofree != NULL) rettv->vval.v_string = (char_u *)tofree; *** ../vim-8.2.2758/src/proto/vim9type.pro 2021-03-18 22:15:00.589208304 +0100 --- src/proto/vim9type.pro 2021-04-13 20:35:14.809994522 +0200 *************** *** 9,15 **** type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap); int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap); int need_convert_to_bool(type_T *type, typval_T *tv); ! type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap); type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap); int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx); int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where); --- 9,15 ---- type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap); int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap); int need_convert_to_bool(type_T *type, typval_T *tv); ! type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member); type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap); int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx); int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where); *** ../vim-8.2.2758/src/list.c 2021-04-08 20:09:49.267143853 +0200 --- src/list.c 2021-04-13 20:35:33.581920676 +0200 *************** *** 2059,2065 **** { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list); } if (argvars[0].v_type == VAR_BLOB) --- 2059,2065 ---- { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list, TRUE); } if (argvars[0].v_type == VAR_BLOB) *************** *** 2565,2571 **** { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list); } if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) --- 2565,2571 ---- { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); ! type = typval2type(argvars, get_copyID(), &type_list, TRUE); } if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) *** ../vim-8.2.2758/src/vim9script.c 2021-04-01 13:17:46.356214572 +0200 --- src/vim9script.c 2021-04-13 20:36:55.257605669 +0200 *************** *** 713,719 **** * When "create" is TRUE this is a new variable, otherwise find and update an * existing variable. * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. ! * When "*type" is NULL use "tv" for the type and update "*type". */ void update_vim9_script_var( --- 713,720 ---- * When "create" is TRUE this is a new variable, otherwise find and update an * existing variable. * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. ! * When "*type" is NULL use "tv" for the type and update "*type". If ! * "do_member" is TRUE also use the member type, otherwise use "any". */ void update_vim9_script_var( *************** *** 721,727 **** dictitem_T *di, int flags, typval_T *tv, ! type_T **type) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; --- 722,729 ---- dictitem_T *di, int flags, typval_T *tv, ! type_T **type, ! int do_member) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; *************** *** 774,780 **** if (sv != NULL) { if (*type == NULL) ! *type = typval2type(tv, get_copyID(), &si->sn_type_list); sv->sv_type = *type; } --- 776,783 ---- if (sv != NULL) { if (*type == NULL) ! *type = typval2type(tv, get_copyID(), &si->sn_type_list, ! do_member); sv->sv_type = *type; } *** ../vim-8.2.2758/src/proto/vim9script.pro 2021-03-31 21:07:21.312591129 +0200 --- src/proto/vim9script.pro 2021-04-13 20:38:08.249331798 +0200 *************** *** 12,18 **** int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); ! void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type); void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); --- 12,18 ---- int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); ! void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type, int do_member); void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); *** ../vim-8.2.2758/src/vim.h 2021-04-10 22:35:40.487360271 +0200 --- src/vim.h 2021-04-13 20:40:16.204866053 +0200 *************** *** 2157,2162 **** --- 2157,2163 ---- #define ASSIGN_NO_DECL 0x04 // "name = expr" without ":let"/":const"/":final" #define ASSIGN_DECL 0x08 // may declare variable if it does not exist #define ASSIGN_UNPACK 0x10 // using [a, b] = list + #define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff *** ../vim-8.2.2758/src/testdir/test_vim9_script.vim 2021-04-10 20:52:39.991416717 +0200 --- src/testdir/test_vim9_script.vim 2021-04-13 20:50:12.098849100 +0200 *************** *** 2295,2364 **** enddef def Test_for_loop() ! var result = '' ! for cnt in range(7) ! if cnt == 4 ! break ! endif ! if cnt == 2 ! continue ! endif ! result ..= cnt .. '_' ! endfor ! assert_equal('0_1_3_', result) ! var concat = '' ! for str in eval('["one", "two"]') ! concat ..= str ! endfor ! assert_equal('onetwo', concat) ! var total = 0 ! for nr in ! [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) ! total = 0 ! for nr ! in [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) ! total = 0 ! for nr ! in ! [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) var res = "" for [n: number, s: string] in [[1, 'a'], [2, 'b']] res ..= n .. s endfor assert_equal('1a2b', res) - - # loop over string - res = '' - for c in 'aéc̀d' - res ..= c .. '-' - endfor - assert_equal('a-é-c̀-d-', res) - - res = '' - for c in '' - res ..= c .. '-' - endfor - assert_equal('', res) - - res = '' - for c in test_null_string() - res ..= c .. '-' - endfor - assert_equal('', res) enddef def Test_for_loop_fails() --- 2295,2376 ---- enddef def Test_for_loop() ! var lines =<< trim END ! var result = '' ! for cnt in range(7) ! if cnt == 4 ! break ! endif ! if cnt == 2 ! continue ! endif ! result ..= cnt .. '_' ! endfor ! assert_equal('0_1_3_', result) ! var concat = '' ! for str in eval('["one", "two"]') ! concat ..= str ! endfor ! assert_equal('onetwo', concat) ! var total = 0 ! for nr in ! [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) ! total = 0 ! for nr ! in [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) ! total = 0 ! for nr ! in ! [1, 2, 3] ! total += nr ! endfor ! assert_equal(6, total) ! ! # loop over string ! var res = '' ! for c in 'aéc̀d' ! res ..= c .. '-' ! endfor ! assert_equal('a-é-c̀-d-', res) ! ! res = '' ! for c in '' ! res ..= c .. '-' ! endfor ! assert_equal('', res) ! ! res = '' ! for c in test_null_string() ! res ..= c .. '-' ! endfor ! assert_equal('', res) ! ! var foo: list> = [ ! {a: 'Cat'} ! ] ! for dd in foo ! dd.counter = 12 ! endfor ! assert_equal([{a: 'Cat', counter: 12}], foo) ! END ! CheckDefAndScriptSuccess(lines) + # TODO: should also work at script level var res = "" for [n: number, s: string] in [[1, 'a'], [2, 'b']] res ..= n .. s endfor assert_equal('1a2b', res) enddef def Test_for_loop_fails() *************** *** 2471,2490 **** enddef def Test_for_loop_with_try_continue() ! var looped = 0 ! var cleanup = 0 ! for i in range(3) ! looped += 1 ! try ! eval [][0] ! catch ! continue ! finally ! cleanup += 1 ! endtry ! endfor ! assert_equal(3, looped) ! assert_equal(3, cleanup) enddef def Test_while_loop() --- 2483,2505 ---- enddef def Test_for_loop_with_try_continue() ! var lines =<< trim END ! var looped = 0 ! var cleanup = 0 ! for i in range(3) ! looped += 1 ! try ! eval [][0] ! catch ! continue ! finally ! cleanup += 1 ! endtry ! endfor ! assert_equal(3, looped) ! assert_equal(3, cleanup) ! END ! CheckDefAndScriptSuccess(lines) enddef def Test_while_loop() *** ../vim-8.2.2758/src/version.c 2021-04-12 22:02:32.308393464 +0200 --- src/version.c 2021-04-13 20:52:40.854365290 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2759, /**/ -- From "know your smileys": [:-) Frankenstein's monster /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///