]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.2054: Can't unpack tuple from imported function v9.1.2054
authorYegappan Lakshmanan <yegappan@yahoo.com>
Tue, 6 Jan 2026 10:13:02 +0000 (10:13 +0000)
committerChristian Brabandt <cb@256bit.org>
Tue, 6 Jan 2026 10:13:02 +0000 (10:13 +0000)
Problem:  Can't unpack tuple from imported function
          (Mao-Yining)
Solution: Support multi-variable assignment from a tuple returned by an
          imported function (Yegappan Lakshmanan)

fixes:  #19080
closes: #19083

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
14 files changed:
src/evalfunc.c
src/proto/vim9compile.pro
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_tuple.vim
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_generics.vim
src/version.c
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c
src/vim9type.c

index 5aeca60d48a469d4d2035f60095cce0146f94379..e67f727d25510240aef6bf7f60465f67d148f82f 100644 (file)
@@ -229,7 +229,7 @@ check_arg_type(
        type_T          *actual,
        argcontext_T    *context)
 {
-    return need_type(actual, expected, FALSE,
+    return need_type(actual, expected, 0,
            context->arg_idx - context->arg_count, context->arg_idx + 1,
            context->arg_cctx, FALSE, FALSE);
 }
@@ -243,7 +243,7 @@ check_arg_type_mod(
        type_T          *actual,
        argcontext_T    *context)
 {
-    if (need_type(actual, expected, FALSE,
+    if (need_type(actual, expected, 0,
            context->arg_idx - context->arg_count, context->arg_idx + 1,
            context->arg_cctx, FALSE, FALSE) == FAIL)
        return FAIL;
index 6d91bc86a02b86fd911b58434bb8985fd2079606..e8b9cdd197e7be784af00027c0e3e3ba36ebfc9a 100644 (file)
@@ -6,8 +6,8 @@ int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack);
 int cctx_class_method_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
 int cctx_class_member_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
 int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg);
-int need_type_where(type_T *actual, type_T *expected, int number_ok, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const);
-int need_type(type_T *actual, type_T *expected, int number_ok, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);
+int need_type_where(type_T *actual, type_T *expected, int typechk_flags, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const);
+int need_type(type_T *actual, type_T *expected, int typechk_flags, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);
 lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int assign, type_T *type);
 int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx, cstack_T *cstack);
 imported_T *find_imported(char_u *name, size_t len, int load);
index a4504c5137ef487123a91cf4ae7035d802764f33..f4b587d8cb1964293b797e36c992f0e5b9f08ab2 100644 (file)
@@ -16,7 +16,7 @@ int generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic);
 int generate_CONCAT(cctx_T *cctx, int count);
 int generate_2BOOL(cctx_T *cctx, int invert, int offset);
 int generate_COND2BOOL(cctx_T *cctx);
-int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int number_ok, int offset, int is_var, int argidx);
+int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int typechk_flags, int offset, int is_var, int argidx);
 int generate_SETTYPE(cctx_T *cctx, type_T *expected);
 int generate_PUSHOBJ(cctx_T *cctx);
 int generate_tv_PUSH(cctx_T *cctx, typval_T *tv);
index 6513f2b89479cffa819c83b109d602fd9d01d044..6df682307bc35e4da5fe151c6d5cd7daf66951f1 100644 (file)
@@ -1532,7 +1532,7 @@ struct type_S {
     vartype_T      tt_type;
     int8_T         tt_argcount;    // for func, incl. vararg, -1 for unknown
     int8_T         tt_min_argcount; // number of non-optional arguments
-    char_u         tt_flags;       // TTFLAG_ values
+    short_u        tt_flags;       // TTFLAG_ values
     type_T         *tt_member;     // for list, dict, func return type
     class_T        *tt_class;      // for class and object
     type_T         **tt_args;      // func argument types, allocated
@@ -1551,10 +1551,15 @@ typedef struct {
 #define TTFLAG_CONST       0x20    // cannot be changed
 #define TTFLAG_SUPER       0x40    // object from "super".
 #define TTFLAG_GENERIC     0x80    // generic type
+#define TTFLAG_TUPLE_OK            0x100   // tuple can be used for a list
 
 #define IS_GENERIC_TYPE(type)  \
     ((type->tt_flags & TTFLAG_GENERIC) == TTFLAG_GENERIC)
 
+// Type check flags
+#define TYPECHK_NUMBER_OK      0x1     // number is accepted for a float
+#define TYPECHK_TUPLE_OK       0x2     // tuple is accepted for a list
+
 typedef enum {
     VIM_ACCESS_PRIVATE,        // read/write only inside the class
     VIM_ACCESS_READ,   // read everywhere, write only inside the class
index 1d87fe1194e262e852b804cff4577ca00e9f2b34..191546a5e4f9305edb1f28d3184c53229a0e9d07 100644 (file)
@@ -2356,4 +2356,103 @@ func Test_tuple2list()
   call v9.CheckSourceDefAndScriptSuccess(lines)
 endfunc
 
+" Test for assigning multiple variables (list assign) using an imported
+" function returning a tuple.
+func Test_tuple_multi_assign_from_import()
+  let lines =<< trim END
+    vim9script
+
+    export def Fn(): tuple<string, string>
+      return ('aaa', 'bbb')
+    enddef
+  END
+  call writefile(lines, 'Ximporttuplefunc.vim', 'D')
+
+  let lines =<< trim END
+    vim9script
+
+    import "./Ximporttuplefunc.vim" as Xtuple
+
+    def XtestTupleListAssign()
+      const [x, y] = Xtuple.Fn()
+      assert_equal(['aaa', 'bbb'], [x, y])
+    enddef
+    XtestTupleListAssign()
+
+    # Check the generated code
+    def XtestTupleListAssign2()
+      const [x, y] = Xtuple.Fn()
+    enddef
+    var res = execute('disass XtestTupleListAssign2')
+    assert_match('<SNR>\d*_XtestTupleListAssign2\_s*' ..
+          'const \[x, y\] = Xtuple.Fn()\_s*' ..
+          '0 PUSHFUNC "<80><fd>R\d\+_Fn"\_s*' ..
+          '1 PCALL top (argc 0)\_s*' ..
+          '2 PCALL end\_s*' ..
+          '3 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
+          '4 CHECKLEN 2\_s*' ..
+          '5 ITEM 0\_s*' ..
+          '6 LOCKCONST\_s*' ..
+          '7 STORE $0\_s*' ..
+          '8 ITEM 1\_s*' ..
+          '9 LOCKCONST\_s*' ..
+          '10 STORE $1\_s*' ..
+          '11 DROP\_s*' ..
+          '12 RETURN void',
+          res)
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+endfunc
+
+" Test for assigning multiple variables in a for loop using an imported
+" function returning a tuple.
+func Test_tuple_multi_assign_in_for_loop_from_import()
+  let lines =<< trim END
+    vim9script
+
+    export def Fn(): tuple<...list<tuple<string, string>>>
+      return (('a', 'b'), ('c', 'd'))
+    enddef
+  END
+  call writefile(lines, 'Ximporttupleinfor.vim', 'D')
+
+  let lines =<< trim END
+    vim9script
+
+    import "./Ximporttupleinfor.vim" as Xtuple
+
+    def XtestTupleInFor()
+      var s = ''
+      for [x, y] in Xtuple.Fn()
+        s ..= x .. y
+      endfor
+      assert_equal('abcd', s)
+    enddef
+    XtestTupleInFor()
+
+    # Check the generated code
+    def XtestTupleInFor2()
+      for [x, y] in Xtuple.Fn()
+      endfor
+    enddef
+    var res = execute('disass XtestTupleInFor2')
+    assert_match('<SNR>\d*_XtestTupleInFor2\_s*' ..
+          'for \[x, y\] in Xtuple.Fn()\_s*' ..
+          '0 STORE -1 in $0\_s*' ..
+          '1 PUSHFUNC "<80><fd>R\d\+_Fn"\_s*' ..
+          '2 PCALL top (argc 0)\_s*' ..
+          '3 PCALL end\_s*' ..
+          '4 FOR $0 -> 9\_s*' ..
+          '5 UNPACK 2\_s*' ..
+          '6 STORE $2\_s*' ..
+          '7 STORE $3\_s*' ..
+          'endfor\_s*' ..
+          '8 JUMP -> 4\_s*' ..
+          '9 DROP\_s*' ..
+          '10 RETURN void',
+          res)
+  END
+  call v9.CheckSourceScriptSuccess(lines)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index c05381fb167ab32fe6ac093691af519b51493b3d..f48f7a23c100fcd5845d3e0e0be056a2e0c23f8e 100644 (file)
@@ -647,7 +647,7 @@ def Test_disassemble_list_assign()
         '\d STORE $2\_s*' ..
         '\[x, y; l\] = g:stringlist\_s*' ..
         '\d LOADG g:stringlist\_s*' ..
-        '\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
+        '\d CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
         '\d CHECKLEN >= 2\_s*' ..
         '\d\+ ITEM 0\_s*' ..
         '\d\+ CHECKTYPE string stack\[-1\] var 1\_s*' ..
index 48b31b043c871f3a31df32beb1702453afa2fda9..9aadd99cfd554c94408574e0c8b22afd1cf740c0 100644 (file)
@@ -2064,7 +2064,7 @@ def Test_generic_function_disassemble()
     '5 STORE $1\_s*' ..
     '\[x, y\] = g:values\_s*' ..
     '6 LOADG g:values\_s*\_s*' ..
-    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
     '8 CHECKLEN 2\_s*' ..
     '9 ITEM 0\_s*' ..
     '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
@@ -2201,7 +2201,7 @@ def Test_generic_disassemble_generic_obj_method()
     '5 STORE $2\_s*' ..
     '\[x, y\] = g:values\_s*' ..
     '6 LOADG g:values\_s*' ..
-    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
     '8 CHECKLEN 2\_s*' ..
     '9 ITEM 0\_s*' ..
     '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
@@ -2329,7 +2329,7 @@ def Test_generic_disassemble_generic_class_method()
     '5 STORE $1\_s*' ..
     '\[x, y\] = g:values\_s*' ..
     '6 LOADG g:values\_s*' ..
-    '7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
+    '7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
     '8 CHECKLEN 2\_s*' ..
     '9 ITEM 0\_s*' ..
     '10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
index a9c9d0544b2b6320855e2be5dc29120fd1542e0a..2f0b907699f69825d38dd9676fee8933b197bd0a 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2054,
 /**/
     2053,
 /**/
index 8a1ab80cd9cf3f0ba61f4259aa18a8d05fc7a17b..be9c67be691ab134b077a4296059e8b59d2a46ae 100644 (file)
@@ -1172,7 +1172,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
                if (lhs_type == &t_any)
                    lhs_type = item_type;
                else if (item_type != &t_unknown
-                       && need_type_where(item_type, lhs_type, FALSE, -1,
+                       && need_type_where(item_type, lhs_type, 0, -1,
                                            where, cctx, FALSE, FALSE) == FAIL)
                    goto failed;
                var_lvar = reserve_local(cctx, arg, varlen, ASSIGN_FINAL,
@@ -2625,7 +2625,7 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
        if (compile_assign_lhs(arg, lhs, CMD_redir,
                                         FALSE, FALSE, FALSE, 1, cctx) == FAIL)
            return NULL;
-       if (need_type(&t_string, lhs->lhs_member_type, FALSE,
+       if (need_type(&t_string, lhs->lhs_member_type, 0,
                                            -1, 0, cctx, FALSE, FALSE) == FAIL)
            return NULL;
        if (cctx->ctx_skip == SKIP_YES)
@@ -2707,7 +2707,7 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx)
            int save_flags = cmdmod.cmod_flags;
 
            generate_LEGACY_EVAL(cctx, p);
-           if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, FALSE, -1,
+           if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, 0, -1,
                                                0, cctx, FALSE, FALSE) == FAIL)
                return NULL;
            cmdmod.cmod_flags |= CMOD_LEGACY;
@@ -2738,7 +2738,7 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx)
            }
            else
            {
-               if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, FALSE,
+               if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, 0,
                                            -1, 0, cctx, FALSE, FALSE) == FAIL)
                    return NULL;
            }
index 1b3c7360ae0cece9d82ad30229c2092fc7b9fd5c..b80eb346e26a32910740058311deaca5dc5613fe 100644 (file)
@@ -526,12 +526,16 @@ use_typecheck(type_T *actual, type_T *expected)
  * - return FAIL.
  * If "actual_is_const" is TRUE then the type won't change at runtime, do not
  * generate a TYPECHECK.
+ * If "typechk_flags" has TYPECHK_NUMBER_OK, then a number is accepted for
+ * a float.
+ * If "typechk_flags" has TYPECHK_TUPLE_OK, then a tuple is accepted for a
+ * list.
  */
     int
 need_type_where(
        type_T  *actual,
        type_T  *expected,
-       int     number_ok,      // expect VAR_FLOAT but VAR_NUMBER is OK
+       int     typechk_flags,  // acceptable types (type check flags)
        int     offset,
        where_T where,
        cctx_T  *cctx,
@@ -567,7 +571,7 @@ need_type_where(
     // If the actual type can be the expected type add a runtime check.
     if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected))
     {
-       generate_TYPECHECK(cctx, expected, number_ok, offset,
+       generate_TYPECHECK(cctx, expected, typechk_flags, offset,
                where.wt_kind == WT_VARIABLE, where.wt_index);
        return OK;
     }
@@ -581,7 +585,7 @@ need_type_where(
 need_type(
        type_T  *actual,
        type_T  *expected,
-       int     number_ok,  // when expected is float number is also OK
+       int     typechk_flags,  // acceptable types (type check flags)
        int     offset,
        int     arg_idx,
        cctx_T  *cctx,
@@ -595,7 +599,7 @@ need_type(
        where.wt_index = arg_idx;
        where.wt_kind = WT_ARGUMENT;
     }
-    return need_type_where(actual, expected, number_ok, offset, where,
+    return need_type_where(actual, expected, typechk_flags, offset, where,
                                                cctx, silent, actual_is_const);
 }
 
@@ -2521,7 +2525,7 @@ compile_load_lhs(
        if (rhs_type != NULL && member_type != NULL
                && vartype != VAR_OBJECT && vartype != VAR_CLASS
                && rhs_type != &t_void
-               && need_type(rhs_type, member_type, FALSE,
+               && need_type(rhs_type, member_type, 0,
                                            -3, 0, cctx, FALSE, FALSE) == FAIL)
            return FAIL;
 
@@ -2680,13 +2684,13 @@ compile_assign_unlet(
            if (range)
            {
                type = get_type_on_stack(cctx, 1);
-               if (need_type(type, &t_number, FALSE,
+               if (need_type(type, &t_number, 0,
                                            -2, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
            }
            type = get_type_on_stack(cctx, 0);
            if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL)
-                   && need_type(type, &t_number, FALSE,
+                   && need_type(type, &t_number, 0,
                                            -1, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
        }
@@ -3041,7 +3045,7 @@ compile_assign_list_check_rhs_type(cctx_T *cctx, cac_T *cac)
 
     if (need_type(stacktype,
                  stacktype->tt_type == VAR_TUPLE ? &t_tuple_any : &t_list_any,
-                 FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
+                 TYPECHK_TUPLE_OK, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
 
     if (stacktype->tt_type == VAR_TUPLE)
@@ -3280,7 +3284,7 @@ compile_assign_valid_rhs_type(
            !has_list_index(cac->cac_var_start + lhs->lhs_varlen, cctx))
        use_type = lhs->lhs_member_type;
 
-    if (need_type_where(rhs_type, use_type, FALSE, -1, where, cctx, FALSE,
+    if (need_type_where(rhs_type, use_type, 0, -1, where, cctx, FALSE,
                                                cac->cac_is_const) == FAIL)
        return FALSE;
 
@@ -3342,7 +3346,7 @@ compile_assign_check_type(cctx_T *cctx, cac_T *cac)
 
        if (*cac->cac_nextc != '=')
        {
-           if (need_type(rhs_type, lhs_type, FALSE, -1, 0, cctx, FALSE,
+           if (need_type(rhs_type, lhs_type, 0, -1, 0, cctx, FALSE,
                                                        FALSE) == FAIL)
                return FAIL;
        }
@@ -3493,7 +3497,7 @@ compile_assign_compound_op(cctx_T *cctx, cac_T *cac)
        expected = lhs->lhs_member_type;
        stacktype = get_type_on_stack(cctx, 0);
        if (expected != &t_string
-               && need_type(stacktype, expected, FALSE, -1, 0, cctx,
+               && need_type(stacktype, expected, 0, -1, 0, cctx,
                                        FALSE, FALSE) == FAIL)
            return FAIL;
        else if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
@@ -3511,8 +3515,8 @@ compile_assign_compound_op(cctx_T *cctx, cac_T *cac)
                // If variable is float operation with number is OK.
                !(expected == &t_float && (stacktype == &t_number
                                        || stacktype == &t_number_bool))
-               && need_type(stacktype, expected, TRUE, -1, 0, cctx,
-                                       FALSE, FALSE) == FAIL)
+               && need_type(stacktype, expected, TYPECHK_NUMBER_OK, -1, 0,
+                                               cctx, FALSE, FALSE) == FAIL)
            return FAIL;
     }
 
@@ -3659,7 +3663,7 @@ compile_assign_process_variables(
            SOURCING_LNUM = cac->cac_start_lnum;
            if (cac->cac_lhs.lhs_has_type
                    && need_type(&t_list_string, cac->cac_lhs.lhs_type,
-                       FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
+                               0, -1, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
        }
        else
@@ -4119,7 +4123,7 @@ obj_constructor_prologue(ufunc_T *ufunc, cctx_T *cctx)
                where_T where = WHERE_INIT;
                where.wt_kind = WT_MEMBER;
                where.wt_func_name = (char *)m->ocm_name.string;
-               if (need_type_where(type, m->ocm_type, FALSE, -1,
+               if (need_type_where(type, m->ocm_type, 0, -1,
                                        where, cctx, FALSE, FALSE) == FAIL)
                    return FAIL;
            }
@@ -4224,7 +4228,7 @@ compile_def_function_default_args(
            ufunc->uf_arg_types[arg_idx] = val_type;
        }
        else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
-                   FALSE, -1, where, cctx, FALSE, FALSE) == FAIL)
+                   0, -1, where, cctx, FALSE, FALSE) == FAIL)
            return FAIL;
 
        if (generate_STORE(cctx, ISN_STORE, i - count - off, NULL) == FAIL)
index 2fc6da8d7574883cfbcf2c98b2653ecfb41d4350..25e56005bcb34a8f61c8b2ba03435447a810714e 100644 (file)
@@ -7871,6 +7871,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                      if (ct->ct_type->tt_type == VAR_FLOAT
                              && (ct->ct_type->tt_flags & TTFLAG_NUMBER_OK))
                          typename = "float|number";
+                     else if (ct->ct_type->tt_type == VAR_LIST
+                             && (ct->ct_type->tt_flags & TTFLAG_TUPLE_OK))
+                         typename = "list<any>|tuple<any>";
                      else
                          typename = type_name(ct->ct_type, &tofree);
 
index a3714e592b5d35da3902f701b704cd60ea2e51c0..c8353ab981f3b2881f72a41d9744024c385a72c9 100644 (file)
@@ -162,13 +162,13 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
     if (vartype == VAR_STRING || vartype == VAR_LIST || vartype == VAR_BLOB
                                                || vartype == VAR_TUPLE)
     {
-       if (need_type(idxtype, &t_number, FALSE,
+       if (need_type(idxtype, &t_number, 0,
                                            -1, 0, cctx, FALSE, FALSE) == FAIL)
            return FAIL;
        if (is_slice)
        {
            idxtype = get_type_on_stack(cctx, 1);
-           if (need_type(idxtype, &t_number, FALSE,
+           if (need_type(idxtype, &t_number, 0,
                                            -2, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
        }
@@ -199,7 +199,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
        }
        else
        {
-           if (need_type(typep->type_curr, &t_dict_any, FALSE,
+           if (need_type(typep->type_curr, &t_dict_any, 0,
                                            -2, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
            typep->type_curr = &t_any;
@@ -2374,7 +2374,7 @@ bool_on_stack(cctx_T *cctx)
        // This requires a runtime type check.
        return generate_COND2BOOL(cctx);
 
-    return need_type(type, &t_bool, FALSE, -1, 0, cctx, FALSE, FALSE);
+    return need_type(type, &t_bool, 0, -1, 0, cctx, FALSE, FALSE);
 }
 
 /*
@@ -2408,7 +2408,7 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
        {
            type_T *type = get_type_on_stack(cctx, 0);
            if (type->tt_type != VAR_FLOAT && need_type(type, &t_number,
-                                    FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
+                                    0, -1, 0, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
 
            // only '-' has an effect, for '+' we only check the type
@@ -3196,8 +3196,8 @@ compile_expr8(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
        actual = get_type_on_stack(cctx, 0);
        if (check_type_maybe(want_type, actual, FALSE, where) != OK)
        {
-           if (need_type_where(actual, want_type, FALSE, -1, where, cctx, FALSE, FALSE)
-                   == FAIL)
+           if (need_type_where(actual, want_type, 0, -1, where, cctx, FALSE,
+                                                       FALSE) == FAIL)
                return FAIL;
        }
     }
@@ -3438,7 +3438,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
        {
            type_T      *t = get_type_on_stack(cctx, 0);
 
-           if (need_type(t, &t_number, FALSE, 0, 0, cctx, FALSE, FALSE) == FAIL)
+           if (need_type(t, &t_number, 0, 0, 0, cctx, FALSE, FALSE) == FAIL)
            {
                emsg(_(e_bitshift_ops_must_be_number));
                return FAIL;
@@ -3493,7 +3493,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
        }
        else
        {
-           if (need_type(get_type_on_stack(cctx, 0), &t_number, FALSE,
+           if (need_type(get_type_on_stack(cctx, 0), &t_number, 0,
                                             0, 0, cctx, FALSE, FALSE) == FAIL)
            {
                emsg(_(e_bitshift_ops_must_be_number));
index 26eedaa2351604ee6fcd6e02d9579cf9502b1f64..c11e913b0a64692d137780a81c66c1461f3b96fe 100644 (file)
@@ -694,7 +694,7 @@ generate_COND2BOOL(cctx_T *cctx)
 generate_TYPECHECK(
        cctx_T      *cctx,
        type_T      *expected,
-       int         number_ok,      // add TTFLAG_NUMBER_OK flag
+       int         typechk_flags,      // type check flags
        int         offset,
        int         is_var,
        int         argidx)
@@ -705,7 +705,10 @@ generate_TYPECHECK(
     if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL)
        return FAIL;
     type_T *tt;
-    if (expected->tt_type == VAR_FLOAT && number_ok)
+    if ((expected->tt_type == VAR_FLOAT
+               && (typechk_flags & TYPECHK_NUMBER_OK))
+           || (expected->tt_type == VAR_LIST
+               && (typechk_flags & TYPECHK_TUPLE_OK)))
     {
        // always allocate, also for static types
        tt = ALLOC_ONE(type_T);
@@ -713,7 +716,10 @@ generate_TYPECHECK(
        {
            *tt = *expected;
            tt->tt_flags &= ~TTFLAG_STATIC;
-           tt->tt_flags |= TTFLAG_NUMBER_OK;
+           if (typechk_flags & TYPECHK_NUMBER_OK)
+               tt->tt_flags |= TTFLAG_NUMBER_OK;
+           else
+               tt->tt_flags |= TTFLAG_TUPLE_OK;
        }
     }
     else
@@ -1846,7 +1852,7 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
     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, FALSE, -1, FALSE, 1);
+       generate_TYPECHECK(cctx, maptype[0].type_decl, 0, -1, FALSE, 1);
 
     return OK;
 }
@@ -1870,7 +1876,7 @@ generate_LISTAPPEND(cctx_T *cctx)
        return FAIL;
     item_type = get_type_on_stack(cctx, 0);
     expected = list_type->tt_member;
-    if (need_type(item_type, expected, FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
+    if (need_type(item_type, expected, 0, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
 
     if (generate_instr(cctx, ISN_LISTAPPEND) == NULL)
@@ -1893,8 +1899,7 @@ generate_BLOBAPPEND(cctx_T *cctx)
     if (arg_type_modifiable(get_decl_type_on_stack(cctx, 1), 1) == FAIL)
        return FAIL;
     item_type = get_type_on_stack(cctx, 0);
-    if (need_type(item_type, &t_number, FALSE,
-                                           -1, 0, cctx, FALSE, FALSE) == FAIL)
+    if (need_type(item_type, &t_number, 0, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
 
     if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL)
@@ -1971,7 +1976,7 @@ generate_CALL(
                expected = &t_any;
            else
                expected = ufunc->uf_va_type->tt_member;
-           if (need_type(actual, expected, FALSE,
+           if (need_type(actual, expected, 0,
                              -argcount + i, i + 1, cctx, TRUE, FALSE) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
@@ -2108,7 +2113,7 @@ check_func_args_from_type(
                    expected = &t_any;
                else
                    expected = type->tt_args[i];
-               if (need_type(actual, expected, FALSE,
+               if (need_type(actual, expected, 0,
                                     offset, i + 1, cctx, TRUE, FALSE) == FAIL)
                {
                    arg_type_mismatch(expected, actual, i + 1);
index 393b852514f7e4bc1b8ace1e98a094f49246676b..938eee00c52795b7a8b61ed085aab0a5497c72ed 100644 (file)
@@ -1372,6 +1372,11 @@ check_type_maybe(
                             || (actual->tt_flags & TTFLAG_FLOAT_OK)))
                // Using a number where a float is expected is OK here.
                return OK;
+           if (expected->tt_type == VAR_LIST
+                   && actual->tt_type == VAR_TUPLE
+                   && (expected->tt_flags & TTFLAG_TUPLE_OK))
+               // Using a tuple where a list is expected is OK here.
+               return OK;
            if (give_msg)
                type_mismatch_where(expected, actual, where);
            return FAIL;