From: Yegappan Lakshmanan Date: Tue, 6 Jan 2026 10:13:02 +0000 (+0000) Subject: patch 9.1.2054: Can't unpack tuple from imported function X-Git-Tag: v9.1.2054^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eb577f9206f621ea77dae175e2af1b0f0a65852f;p=thirdparty%2Fvim.git patch 9.1.2054: Can't unpack tuple from imported function 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 Signed-off-by: Christian Brabandt --- diff --git a/src/evalfunc.c b/src/evalfunc.c index 5aeca60d48..e67f727d25 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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; diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index 6d91bc86a0..e8b9cdd197 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -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); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index a4504c5137..f4b587d8cb 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -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); diff --git a/src/structs.h b/src/structs.h index 6513f2b894..6df682307b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/testdir/test_tuple.vim b/src/testdir/test_tuple.vim index 1d87fe1194..191546a5e4 100644 --- a/src/testdir/test_tuple.vim +++ b/src/testdir/test_tuple.vim @@ -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 + 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('\d*_XtestTupleListAssign2\_s*' .. + 'const \[x, y\] = Xtuple.Fn()\_s*' .. + '0 PUSHFUNC "<80>R\d\+_Fn"\_s*' .. + '1 PCALL top (argc 0)\_s*' .. + '2 PCALL end\_s*' .. + '3 CHECKTYPE list|tuple 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>> + 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('\d*_XtestTupleInFor2\_s*' .. + 'for \[x, y\] in Xtuple.Fn()\_s*' .. + '0 STORE -1 in $0\_s*' .. + '1 PUSHFUNC "<80>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 diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index c05381fb16..f48f7a23c1 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -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 stack\[-1\]\_s*' .. + '\d CHECKTYPE list|tuple stack\[-1\]\_s*' .. '\d CHECKLEN >= 2\_s*' .. '\d\+ ITEM 0\_s*' .. '\d\+ CHECKTYPE string stack\[-1\] var 1\_s*' .. diff --git a/src/testdir/test_vim9_generics.vim b/src/testdir/test_vim9_generics.vim index 48b31b043c..9aadd99cfd 100644 --- a/src/testdir/test_vim9_generics.vim +++ b/src/testdir/test_vim9_generics.vim @@ -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 stack\[-1\]\_s*' .. + '7 CHECKTYPE list|tuple stack\[-1\]\_s*' .. '8 CHECKLEN 2\_s*' .. '9 ITEM 0\_s*' .. '10 CHECKTYPE list 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 stack\[-1\]\_s*' .. + '7 CHECKTYPE list|tuple stack\[-1\]\_s*' .. '8 CHECKLEN 2\_s*' .. '9 ITEM 0\_s*' .. '10 CHECKTYPE list 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 stack\[-1\]\_s*' .. + '7 CHECKTYPE list|tuple stack\[-1\]\_s*' .. '8 CHECKLEN 2\_s*' .. '9 ITEM 0\_s*' .. '10 CHECKTYPE list stack\[-1\] var 1\_s*' .. diff --git a/src/version.c b/src/version.c index a9c9d0544b..2f0b907699 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2054, /**/ 2053, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 8a1ab80cd9..be9c67be69 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -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; } diff --git a/src/vim9compile.c b/src/vim9compile.c index 1b3c7360ae..b80eb346e2 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -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) diff --git a/src/vim9execute.c b/src/vim9execute.c index 2fc6da8d75..25e56005bc 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -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|tuple"; else typename = type_name(ct->ct_type, &tofree); diff --git a/src/vim9expr.c b/src/vim9expr.c index a3714e592b..c8353ab981 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -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)); diff --git a/src/vim9instr.c b/src/vim9instr.c index 26eedaa235..c11e913b0a 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -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); diff --git a/src/vim9type.c b/src/vim9type.c index 393b852514..938eee00c5 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -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;