Patch 8.2.3991
Problem:    Vim9: error when extending dict<any> with another type that it was
            initialized with.
Solution:   Also set the type for dict<any> if the initializer has a more
            specific type. (closes #9461)
Files:      src/vim9compile.c, src/vim9type.c, src/vim9.h, src/eval.c,
            src/list.c, src/vim9script.c, src/testdir/test_vim9_assign.vim,
            src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_func.vim


*** ../vim-8.2.3990/src/vim9compile.c   2022-01-01 15:58:19.122486356 +0000
--- src/vim9compile.c   2022-01-03 11:34:36.005461730 +0000
***************
*** 2286,2296 ****
                    && (lhs.lhs_type->tt_type == VAR_DICT
                                          || lhs.lhs_type->tt_type == VAR_LIST)
                    && lhs.lhs_type->tt_member != NULL
-                   && !(lhs.lhs_type->tt_member == &t_any
-                           && oplen > 0
-                           && rhs_type != NULL
-                           && rhs_type->tt_type == lhs.lhs_type->tt_type
-                           && rhs_type->tt_member != &t_unknown)
                    && lhs.lhs_type->tt_member != &t_unknown)
                // Set the type in the list or dict, so that it can be checked,
                // also in legacy script.  Not for "list<any> = val", then the
--- 2286,2291 ----
*** ../vim-8.2.3990/src/vim9type.c      2021-12-30 13:59:17.038036582 +0000
--- src/vim9type.c      2022-01-03 12:21:04.425371033 +0000
***************
*** 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 = NULL;
--- 252,264 ----
  /*
   * Get a type_T for a typval_T.
   * "type_gap" is used to temporarily create types in.
!  * When "flags" has TVTT_DO_MEMBER also get the member type, otherwise use
!  * "any".
!  * When "flags" has TVTT_MORE_SPECIFIC get the more specific member type if it
!  * is "any".
   */
      static type_T *
! typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
  {
      type_T  *type;
      type_T  *member_type = NULL;
***************
*** 278,286 ****
  
        if (l == NULL || (l->lv_first == NULL && l->lv_type == NULL))
            return &t_list_empty;
!       if (!do_member)
            return &t_list_any;
!       if (l->lv_type != NULL)
            return l->lv_type;
        if (l->lv_first == &range_list_item)
            return &t_list_number;
--- 281,293 ----
  
        if (l == NULL || (l->lv_first == NULL && l->lv_type == NULL))
            return &t_list_empty;
!       if ((flags & TVTT_DO_MEMBER) == 0)
            return &t_list_any;
!       // If the type is list<any> go through the members, it may end up a
!       // more specific type.
!       if (l->lv_type != NULL && (l->lv_first == NULL
!                                          || (flags & TVTT_MORE_SPECIFIC) == 0
!                                          || l->lv_type->tt_member != &t_any))
            return l->lv_type;
        if (l->lv_first == &range_list_item)
            return &t_list_number;
***************
*** 290,298 ****
        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,307 ----
        l->lv_copyID = copyID;
  
        // Use the common type of all members.
!       member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap,
!                                                              TVTT_DO_MEMBER);
        for (li = l->lv_first->li_next; li != NULL; li = li->li_next)
!           common_type(typval2type(&li->li_tv, copyID, type_gap,
!                                                              TVTT_DO_MEMBER),
                                          member_type, &member_type, type_gap);
        return get_list_type(member_type, type_gap);
      }
***************
*** 305,313 ****
  
        if (d == NULL || (d->dv_hashtab.ht_used == 0 && d->dv_type == NULL))
            return &t_dict_empty;
!       if (!do_member)
            return &t_dict_any;
!       if (d->dv_type != NULL)
            return d->dv_type;
        if (d->dv_copyID == copyID)
            // avoid recursion
--- 314,326 ----
  
        if (d == NULL || (d->dv_hashtab.ht_used == 0 && d->dv_type == NULL))
            return &t_dict_empty;
!       if ((flags & TVTT_DO_MEMBER) == 0)
            return &t_dict_any;
!       // If the type is dict<any> go through the members, it may end up a
!       // more specific type.
!       if (d->dv_type != NULL && (d->dv_hashtab.ht_used == 0
!                                          || (flags & TVTT_MORE_SPECIFIC) == 0
!                                          || d->dv_type->tt_member != &t_any))
            return d->dv_type;
        if (d->dv_copyID == copyID)
            // avoid recursion
***************
*** 317,325 ****
        // 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);
      }
--- 330,338 ----
        // Use the common type of all values.
        dict_iterate_start(tv, &iter);
        dict_iterate_next(&iter, &value);
!       member_type = typval2type(value, copyID, type_gap, TVTT_DO_MEMBER);
        while (dict_iterate_next(&iter, &value) != NULL)
!           common_type(typval2type(value, copyID, type_gap, TVTT_DO_MEMBER),
                                          member_type, &member_type, type_gap);
        return get_dict_type(member_type, type_gap);
      }
***************
*** 424,435 ****
  /*
   * 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
--- 437,450 ----
  /*
   * Get a type_T for a typval_T.
   * "type_list" is used to temporarily create types in.
!  * When "flags" has TVTT_DO_MEMBER also get the member type, otherwise use
!  * "any".
!  * When "flags" has TVTT_MORE_SPECIFIC get the most specific member type.
   */
      type_T *
! typval2type(typval_T *tv, int copyID, garray_T *type_gap, int flags)
  {
!     type_T *type = typval2type_int(tv, copyID, type_gap, flags);
  
      if (type != NULL && type != &t_bool
            && (tv->v_type == VAR_NUMBER
***************
*** 451,457 ****
        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
--- 466,472 ----
        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, TVTT_DO_MEMBER);
  }
  
      int
***************
*** 493,499 ****
      }
  
      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_maybe(expected, actual_type, TRUE, where);
--- 508,518 ----
      }
  
      ga_init2(&type_list, sizeof(type_T *), 10);
! 
!     // When the actual type is list<any> or dict<any> go through the values to
!     // possibly get a more specific type.
!     actual_type = typval2type(actual_tv, get_copyID(), &type_list,
!                                         TVTT_DO_MEMBER | TVTT_MORE_SPECIFIC);
      if (actual_type != NULL)
      {
        res = check_type_maybe(expected, actual_type, TRUE, where);
***************
*** 1346,1352 ****
  
      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;
--- 1365,1371 ----
  
      rettv->v_type = VAR_STRING;
      ga_init2(&type_list, sizeof(type_T *), 10);
!     type = typval2type(argvars, get_copyID(), &type_list, TVTT_DO_MEMBER);
      name = type_name(type, &tofree);
      if (tofree != NULL)
        rettv->vval.v_string = (char_u *)tofree;
*** ../vim-8.2.3990/src/vim9.h  2021-12-27 17:21:38.024449102 +0000
--- src/vim9.h  2022-01-03 12:08:12.555696828 +0000
***************
*** 725,727 ****
--- 725,731 ----
                                    // lhs_name is not NULL
  };
  
+ // flags for typval2type()
+ #define TVTT_DO_MEMBER            1
+ #define TVTT_MORE_SPECIFIC  2 // get most specific type for member
+ 
*** ../vim-8.2.3990/src/eval.c  2022-01-01 21:59:11.030521935 +0000
--- src/eval.c  2022-01-03 11:56:32.197699034 +0000
***************
*** 3328,3334 ****
      {
        if (res == OK)
        {
!           type_T *actual = typval2type(rettv, get_copyID(), &type_list, TRUE);
  
            if (!equal_type(want_type, actual, 0))
            {
--- 3328,3335 ----
      {
        if (res == OK)
        {
!           type_T *actual = typval2type(rettv, get_copyID(), &type_list,
!                                                              TVTT_DO_MEMBER);
  
            if (!equal_type(want_type, actual, 0))
            {
*** ../vim-8.2.3990/src/list.c  2022-01-01 16:20:56.900401501 +0000
--- src/list.c  2022-01-03 11:58:33.841351277 +0000
***************
*** 2478,2484 ****
      {
        // 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
--- 2478,2484 ----
      {
        // 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, TVTT_DO_MEMBER);
      }
  
      if (argvars[0].v_type != VAR_BLOB
***************
*** 2763,2771 ****
  
      if (!is_new && in_vim9script())
      {
!       // 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)
--- 2763,2771 ----
  
      if (!is_new && in_vim9script())
      {
!       // Check that extend() does not change the type of the dict.
        ga_init2(&type_list, sizeof(type_T *), 10);
!       type = typval2type(argvars, get_copyID(), &type_list, TVTT_DO_MEMBER);
      }
  
      if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
*** ../vim-8.2.3990/src/vim9script.c    2022-01-01 14:19:44.056353820 +0000
--- src/vim9script.c    2022-01-03 11:59:05.885259674 +0000
***************
*** 907,913 ****
      if (sv != NULL)
      {
        if (*type == NULL)
!           *type = typval2type(tv, get_copyID(), &si->sn_type_list, do_member);
        if (sv->sv_type_allocated)
            free_type(sv->sv_type);
        if (*type != NULL && ((*type)->tt_type == VAR_FUNC
--- 907,914 ----
      if (sv != NULL)
      {
        if (*type == NULL)
!           *type = typval2type(tv, get_copyID(), &si->sn_type_list,
!                                              do_member ? TVTT_DO_MEMBER : 0);
        if (sv->sv_type_allocated)
            free_type(sv->sv_type);
        if (*type != NULL && ((*type)->tt_type == VAR_FUNC
*** ../vim-8.2.3990/src/testdir/test_vim9_assign.vim    2021-12-31 
14:06:41.413853990 +0000
--- src/testdir/test_vim9_assign.vim    2022-01-03 12:11:59.183512373 +0000
***************
*** 757,762 ****
--- 757,766 ----
    # type becomes list<any>
    var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
  
+   # type is list<any> even though initializer is list<number>
+   var anyList: list<any> = [0]
+   assert_equal([0, 'x'], extend(anyList, ['x']))
+ 
    var lines =<< trim END
      var d = {dd: test_null_list()}
      d.dd[0] = 0
***************
*** 955,960 ****
--- 959,968 ----
    # type becomes dict<any>
    var somedict = rand() > 0 ? {a: 1, b: 2} : {a: 'a', b: 'b'}
  
+   # type is dict<any> even though initializer is dict<number>
+   var anyDict: dict<any> = {a: 0}
+   assert_equal({a: 0, b: 'x'}, extend(anyDict, {b: 'x'}))
+ 
    # assignment to script-local dict
    lines =<< trim END
      vim9script
*** ../vim-8.2.3990/src/testdir/test_vim9_builtin.vim   2022-01-02 
16:16:30.523363109 +0000
--- src/testdir/test_vim9_builtin.vim   2022-01-03 12:27:09.182904998 +0000
***************
*** 2143,2148 ****
--- 2143,2153 ----
      CheckDefAndScriptFailure(['map(test_null_channel(), "1")'], ['E1013: 
Argument 1: type mismatch, expected list<any> but got channel', 'E1251: List, 
Dictionary, Blob or String required for argument 1'])
    endif
    CheckDefAndScriptFailure(['map(1, "1")'], ['E1013: Argument 1: type 
mismatch, expected list<any> but got number', 'E1251: List, Dictionary, Blob or 
String required for argument 1'])
+ 
+   # type of dict remains dict<any> even when type of values changes
+   var d: dict<any> = {a: 0}
+   d->map((k, v) => true)
+   d->map((k, v) => 'x')
  enddef
  
  def Test_map_failure()
*** ../vim-8.2.3990/src/testdir/test_vim9_func.vim      2021-12-31 
14:06:41.413853990 +0000
--- src/testdir/test_vim9_func.vim      2022-01-03 12:23:44.998186773 +0000
***************
*** 439,444 ****
--- 439,446 ----
  enddef
  
  def Test_return_list_any()
+   # This used to fail but now the actual list type is checked, and since it 
has
+   # an item of type string it can be used as list<string>.
    var lines =<< trim END
        vim9script
        def Func(): list<string>
***************
*** 448,454 ****
        enddef
        echo Func()
    END
!   CheckScriptFailure(lines, 'E1012:')
    lines =<< trim END
        vim9script
        def Func(): list<string>
--- 450,457 ----
        enddef
        echo Func()
    END
!   CheckScriptSuccess(lines)
! 
    lines =<< trim END
        vim9script
        def Func(): list<string>
***************
*** 458,464 ****
        enddef
        echo Func()
    END
!   CheckScriptFailure(lines, 'E1012:')
  enddef
  
  func Increment()
--- 461,467 ----
        enddef
        echo Func()
    END
!   CheckScriptSuccess(lines)
  enddef
  
  func Increment()
*** ../vim-8.2.3990/src/version.c       2022-01-03 11:15:40.756705643 +0000
--- src/version.c       2022-01-03 11:36:35.685119603 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     3991,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
201. When somebody asks you where you are, you tell them in which chat room.

 /// Bram Moolenaar -- [email protected] -- 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220103122831.823851C0A5B%40moolenaar.net.

Raspunde prin e-mail lui