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.