changeset 31742:f578bef02249 v9.0.1203

patch 9.0.1203: return type of values() is always list<any> Commit: https://github.com/vim/vim/commit/32517c4c14ed3f9240fcd5b7c01d0ca2e586f7e4 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 15 18:17:12 2023 +0000 patch 9.0.1203: return type of values() is always list<any> Problem: Return type of values() is always list<any>. Solution: Use the member type if possible. (issue https://github.com/vim/vim/issues/11822)
author Bram Moolenaar <Bram@vim.org>
date Sun, 15 Jan 2023 19:30:04 +0100
parents 47e20df6fce5
children 3ebf441d9f88
files src/evalfunc.c src/proto/evalfunc.pro src/testdir/test_vim9_builtin.vim src/version.c src/vim9instr.c src/vim9type.c
diffstat 6 files changed, 62 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1136,6 +1136,8 @@ static argcheck_T arg23_win_execute[] = 
 static argcheck_T arg23_writefile[] = {arg_list_or_blob, arg_string, arg_string};
 static argcheck_T arg24_match_func[] = {arg_string_or_list_any, arg_string, arg_number, arg_number};
 
+// Can be used by functions called through "f_retfunc" to create new types.
+static garray_T *current_type_gap = NULL;
 
 /*
  * Functions that return the return type of a builtin function.
@@ -1438,6 +1440,29 @@ ret_finddir(int argcount,
     // Depending on the count would be a string or a list of strings.
     return &t_any;
 }
+// for values(): list of member of first argument
+    static type_T *
+ret_list_member(int argcount,
+	type2_T *argtypes,
+	type_T	**decl_type)
+{
+    if (argcount > 0)
+    {
+	type_T *t = argtypes[0].type_decl;
+	if (current_type_gap != NULL
+		&& (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST))
+	    t = get_list_type(t->tt_member, current_type_gap);
+	else
+	    t = &t_list_any;
+	*decl_type = t;
+
+	t = argtypes[0].type_curr;
+	if (current_type_gap != NULL
+		&& (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST))
+	    return get_list_type(t->tt_member, current_type_gap);
+    }
+    return &t_list_any;
+}
 
 /*
  * Used for getqflist(): returns list if there is no argument, dict if there is
@@ -2759,7 +2784,7 @@ static funcentry_T global_functions[] =
     {"uniq",		1, 3, FEARG_1,	    arg13_sortuniq,
 			ret_first_arg,	    f_uniq},
     {"values",		1, 1, FEARG_1,	    arg1_dict_any,
-			ret_list_any,	    f_values},
+			ret_list_member,    f_values},
     {"virtcol",		1, 2, FEARG_1,	    arg2_string_or_list_bool,
 			ret_virtcol,	    f_virtcol},
     {"virtcol2col",	3, 3, FEARG_1,	    arg3_number,
@@ -2993,14 +3018,17 @@ internal_func_ret_type(
 	int	    idx,
 	int	    argcount,
 	type2_T	    *argtypes,
-	type_T	    **decl_type)
+	type_T	    **decl_type,
+	garray_T    *type_gap)
 {
     type_T *ret;
 
+    current_type_gap = type_gap;
     *decl_type = NULL;
     ret = global_functions[idx].f_retfunc(argcount, argtypes, decl_type);
     if (*decl_type == NULL)
 	*decl_type = ret;
+    current_type_gap = NULL;
     return ret;
 }
 
--- a/src/proto/evalfunc.pro
+++ b/src/proto/evalfunc.pro
@@ -7,7 +7,7 @@ int has_internal_func(char_u *name);
 char *internal_func_name(int idx);
 int internal_func_check_arg_types(type2_T *types, int idx, int argcount, cctx_T *cctx);
 void internal_func_get_argcount(int idx, int *argcount, int *min_argcount);
-type_T *internal_func_ret_type(int idx, int argcount, type2_T *argtypes, type_T **decl_type);
+type_T *internal_func_ret_type(int idx, int argcount, type2_T *argtypes, type_T **decl_type, garray_T *type_gap);
 int internal_func_is_map(int idx);
 int check_internal_func(int idx, int argcount);
 int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -4740,6 +4740,31 @@ def Test_values()
   v9.CheckDefAndScriptFailure(['values([])'], ['E1013: Argument 1: type mismatch, expected dict<any> but got list<unknown>', 'E1206: Dictionary required for argument 1'])
   assert_equal([], {}->values())
   assert_equal(['sun'], {star: 'sun'}->values())
+
+  # the return type of values() is list<member>
+  var lines =<< trim END
+      vim9script
+
+      class Foo
+        this.val: number
+        def Add()
+          echo this.val
+        enddef
+      endclass
+
+      def Process(FooDict: dict<Foo>)
+        for foo in values(FooDict)
+          foo.Add()
+        endfor
+      enddef
+
+      disas Process
+
+      var D = {'x': Foo.new(22)}
+
+      Process(D)
+  END
+  v9.CheckScriptSuccess(lines)
 enddef
 
 def Test_virtcol()
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1203,
+/**/
     1202,
 /**/
     1201,
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1619,7 +1619,8 @@ generate_BCALL(cctx_T *cctx, int func_id
 
     // Drop the argument types and push the return type.
     stack->ga_len -= argcount;
-    type = internal_func_ret_type(func_idx, argcount, argtypes, &decl_type);
+    type = internal_func_ret_type(func_idx, argcount, argtypes, &decl_type,
+							  cctx->ctx_type_list);
     if (push_type_stack2(cctx, type, decl_type) == FAIL)
 	return FAIL;
 
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -539,7 +539,8 @@ typval2type_int(typval_T *tv, int copyID
 		type_T *decl_type;  // unused
 
 		internal_func_get_argcount(idx, &argcount, &min_argcount);
-		member_type = internal_func_ret_type(idx, 0, NULL, &decl_type);
+		member_type = internal_func_ret_type(idx, 0, NULL, &decl_type,
+								     type_gap);
 	    }
 	    else
 		ufunc = find_func(name, FALSE);