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<dict<any>> = [
!               {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 -- b...@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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202104131854.13DIsph21051991%40masaka.moolenaar.net.

Raspunde prin e-mail lui