Patch 8.2.4231
Problem: Vim9: map() gives type error when type was not declared.
Solution: Only check the type when it was declared, like extend() does.
(closes #9635)
Files: src/list.c, src/evalfunc.c, src/vim9instr.c,
src/testdir/test_vim9_builtin.vim,
src/testdir/test_vim9_assign.vim
*** ../vim-8.2.4230/src/list.c 2022-01-08 16:19:18.505639885 +0000
--- src/list.c 2022-01-27 15:35:42.761597288 +0000
***************
*** 580,594 ****
{
// empty list
l->lv_first = item;
- l->lv_u.mat.lv_last = item;
item->li_prev = NULL;
}
else
{
l->lv_u.mat.lv_last->li_next = item;
item->li_prev = l->lv_u.mat.lv_last;
- l->lv_u.mat.lv_last = item;
}
++l->lv_len;
item->li_next = NULL;
}
--- 580,593 ----
{
// empty list
l->lv_first = item;
item->li_prev = NULL;
}
else
{
l->lv_u.mat.lv_last->li_next = item;
item->li_prev = l->lv_u.mat.lv_last;
}
+ l->lv_u.mat.lv_last = item;
++l->lv_len;
item->li_next = NULL;
}
***************
*** 2362,2369 ****
tv.v_lock = 0;
tv.vval.v_number = val;
set_vim_var_nr(VV_KEY, idx);
! if (filter_map_one(&tv, expr, filtermap, &newtv, &rem)
! == FAIL)
break;
if (did_emsg)
{
--- 2361,2367 ----
tv.v_lock = 0;
tv.vval.v_number = val;
set_vim_var_nr(VV_KEY, idx);
! if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL)
break;
if (did_emsg)
{
***************
*** 2461,2467 ****
: N_("filter() argument"));
int save_did_emsg;
type_T *type = NULL;
- garray_T type_list;
// map() and filter() return the first argument, also on failure.
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
--- 2459,2464 ----
***************
*** 2474,2482 ****
if (filtermap == FILTERMAP_MAP && 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, TVTT_DO_MEMBER);
}
if (argvars[0].v_type != VAR_BLOB
--- 2471,2483 ----
if (filtermap == FILTERMAP_MAP && in_vim9script())
{
! // Check that map() does not change the declared type of the list or
! // dict.
! if (argvars[0].v_type == VAR_DICT && argvars[0].vval.v_dict != NULL)
! type = argvars[0].vval.v_dict->dv_type;
! else if (argvars[0].v_type == VAR_LIST
! && argvars[0].vval.v_list != NULL)
! type = argvars[0].vval.v_list->lv_type;
}
if (argvars[0].v_type != VAR_BLOB
***************
*** 2489,2498 ****
goto theend;
}
- expr = &argvars[1];
// On type errors, the preceding call has already displayed an error
// message. Avoid a misleading error message for an empty string that
// was not passed as argument.
if (expr->v_type != VAR_UNKNOWN)
{
typval_T save_val;
--- 2490,2499 ----
goto theend;
}
// On type errors, the preceding call has already displayed an error
// message. Avoid a misleading error message for an empty string that
// was not passed as argument.
+ expr = &argvars[1];
if (expr->v_type != VAR_UNKNOWN)
{
typval_T save_val;
***************
*** 2525,2532 ****
}
theend:
- if (type != NULL)
- clear_type_list(&type_list);
}
/*
--- 2526,2531 ----
*** ../vim-8.2.4230/src/evalfunc.c 2022-01-27 13:16:54.328078845 +0000
--- src/evalfunc.c 2022-01-27 16:16:45.417594878 +0000
***************
*** 530,550 ****
if (type->tt_type == VAR_FUNC)
{
! if (type->tt_member != &t_any
! && type->tt_member != &t_unknown)
{
type_T *expected = NULL;
if (context->arg_types[0].type_curr->tt_type == VAR_LIST
|| context->arg_types[0].type_curr->tt_type == VAR_DICT)
! expected = context->arg_types[0].type_curr->tt_member;
else if (context->arg_types[0].type_curr->tt_type == VAR_STRING)
expected = &t_string;
else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB)
expected = &t_number;
if (expected != NULL)
{
! type_T t_func_exp = {VAR_FUNC, -1, 0, TTFLAG_STATIC, NULL, NULL};
t_func_exp.tt_member = expected;
return check_arg_type(&t_func_exp, type, context);
--- 530,559 ----
if (type->tt_type == VAR_FUNC)
{
! if (type->tt_member != &t_any && type->tt_member != &t_unknown)
{
type_T *expected = NULL;
if (context->arg_types[0].type_curr->tt_type == VAR_LIST
|| context->arg_types[0].type_curr->tt_type == VAR_DICT)
! {
! // Use the declared type, so that an error is given if a
! // declared list changes type, but not if a constant list
! // changes type.
! if (context->arg_types[0].type_decl->tt_type == VAR_LIST
! || context->arg_types[0].type_decl->tt_type == VAR_DICT)
! expected = context->arg_types[0].type_decl->tt_member;
! else
! expected = context->arg_types[0].type_curr->tt_member;
! }
else if (context->arg_types[0].type_curr->tt_type == VAR_STRING)
expected = &t_string;
else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB)
expected = &t_number;
if (expected != NULL)
{
! type_T t_func_exp = {VAR_FUNC, -1, 0, TTFLAG_STATIC,
! NULL, NULL};
t_func_exp.tt_member = expected;
return check_arg_type(&t_func_exp, type, context);
*** ../vim-8.2.4230/src/vim9instr.c 2022-01-25 15:51:52.430855187 +0000
--- src/vim9instr.c 2022-01-27 16:22:14.032604969 +0000
***************
*** 1331,1340 ****
if (push_type_stack(cctx, type) == FAIL)
return FAIL;
! if (maptype != NULL && maptype[0].type_curr->tt_member != NULL
! && maptype[0].type_curr->tt_member != &t_any)
// Check that map() didn't change the item types.
! generate_TYPECHECK(cctx, maptype[0].type_curr, -1, 1);
return OK;
}
--- 1331,1340 ----
if (push_type_stack(cctx, type) == FAIL)
return FAIL;
! if (maptype != NULL && maptype[0].type_decl->tt_member != NULL
! && maptype[0].type_decl->tt_member != &t_any)
// Check that map() didn't change the item types.
! generate_TYPECHECK(cctx, maptype[0].type_decl, -1, 1);
return OK;
}
*** ../vim-8.2.4230/src/testdir/test_vim9_builtin.vim 2022-01-27 13:55:29.850840894 +0000
--- src/testdir/test_vim9_builtin.vim 2022-01-27 15:43:27.558970263 +0000
***************
*** 2230,2242 ****
END
CheckDefExecAndScriptFailure(lines, 'E1190: 2 arguments too few')
lines =<< trim END
def Map(i: number, v: number): string
return 'bad'
enddef
! echo map([1, 2, 3], Map)
END
CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(number, number): string', 'E1012: Type mismatch; expected number but got string in map()'])
enddef
def Test_map_item_type()
--- 2230,2247 ----
END
CheckDefExecAndScriptFailure(lines, 'E1190: 2 arguments too few')
+ # declared list cannot change type
lines =<< trim END
def Map(i: number, v: number): string
return 'bad'
enddef
! var ll: list<number> = [1, 2, 3]
! echo map(ll, Map)
END
CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(number, number): string', 'E1012: Type mismatch; expected number but got string in map()'])
+
+ # not declared list can change type
+ echo [1, 2, 3]->map((..._) => 'x')
enddef
def Test_map_item_type()
*** ../vim-8.2.4230/src/testdir/test_vim9_assign.vim 2022-01-15 21:44:39.832970792 +0000
--- src/testdir/test_vim9_assign.vim 2022-01-27 16:26:09.472882359 +0000
***************
*** 1963,1969 ****
var ll: list<number>
ll = [1, 2, 3]->map('"one"')
END
! CheckDefExecFailure(lines, 'E1012: Type mismatch; expected number but got string')
enddef
def Test_cannot_use_let()
--- 1963,1969 ----
var ll: list<number>
ll = [1, 2, 3]->map('"one"')
END
! CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>')
enddef
def Test_cannot_use_let()
*** ../vim-8.2.4230/src/version.c 2022-01-27 15:04:19.004952329 +0000
--- src/version.c 2022-01-27 15:36:50.440630593 +0000
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 4231,
/**/
--
A year spent in artificial intelligence is enough to make one
believe in God.
/// Bram Moolenaar -- Br...@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 ///