]> granicus.if.org Git - vim/commitdiff
patch 8.2.4231: Vim9: map() gives type error when type was not declared v8.2.4231
authorBram Moolenaar <Bram@vim.org>
Thu, 27 Jan 2022 16:36:29 +0000 (16:36 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 27 Jan 2022 16:36:29 +0000 (16:36 +0000)
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)

src/evalfunc.c
src/list.c
src/testdir/test_vim9_assign.vim
src/testdir/test_vim9_builtin.vim
src/version.c
src/vim9instr.c

index 627ebb0b085dc453c6148f9d096d08bdde9938c0..411d332997125b5f270827e2c662fb1bc71afc7e 100644 (file)
@@ -530,21 +530,30 @@ arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
 
     if (type->tt_type == VAR_FUNC)
     {
-       if (type->tt_member != &t_any
-           && type->tt_member != &t_unknown)
+       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;
+           {
+               // 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};
+               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);
index dca5ff0172420bc6751d3cefc94aeb6a9e734f02..a8b22077f6fdfcf387dfc46461ddbc3e1dd56384 100644 (file)
@@ -580,15 +580,14 @@ list_append(list_T *l, listitem_T *item)
     {
        // 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_u.mat.lv_last = item;
     ++l->lv_len;
     item->li_next = NULL;
 }
@@ -2362,8 +2361,7 @@ list_filter_map(
            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)
+           if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL)
                break;
            if (did_emsg)
            {
@@ -2461,7 +2459,6 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
                                                    : 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)
@@ -2474,9 +2471,13 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
 
     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);
+       // 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,10 +2490,10 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
        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.
+    expr = &argvars[1];
     if (expr->v_type != VAR_UNKNOWN)
     {
        typval_T        save_val;
@@ -2525,8 +2526,6 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
     }
 
 theend:
-    if (type != NULL)
-       clear_type_list(&type_list);
 }
 
 /*
index 7f4d87ab3feafde4329b07a5ee91cf2930289705..0af1d5bb020970c8d7a3893ec74b5ae2eb9b4b75 100644 (file)
@@ -1963,7 +1963,7 @@ def Test_var_list_dict_type()
       var ll: list<number>
       ll = [1, 2, 3]->map('"one"')
   END
-  CheckDefExecFailure(lines, 'E1012: Type mismatch; expected number but got string')
+  CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>')
 enddef
 
 def Test_cannot_use_let()
index 45d212cf05ed35d973aa48b1edd88bec089fabc7..32467b4725826fb040dd6da0d53fdeb361a20568 100644 (file)
@@ -2230,13 +2230,18 @@ def Test_map_function_arg()
   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
-    echo map([1, 2, 3], Map)
+    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()
index cbc938a4c723e7eda615960f166f52bbcd270665..7341fcc837ac1bbe3a792e7dacda809077a844a6 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4231,
 /**/
     4230,
 /**/
index d000c4acd68a4a2168d5c0f48b36f32de7723fb5..e869d9ea68d65bd82b44c59f7e6c3c061e127f4c 100644 (file)
@@ -1331,10 +1331,10 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
     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)
+    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_curr, -1, 1);
+       generate_TYPECHECK(cctx, maptype[0].type_decl, -1, 1);
 
     return OK;
 }