Skip to content

Commit eb577f9

Browse files
yegappanchrisbra
authored andcommitted
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 <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent 2a3b608 commit eb577f9

14 files changed

Lines changed: 171 additions & 48 deletions

src/evalfunc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ check_arg_type(
229229
type_T *actual,
230230
argcontext_T *context)
231231
{
232-
return need_type(actual, expected, FALSE,
232+
return need_type(actual, expected, 0,
233233
context->arg_idx - context->arg_count, context->arg_idx + 1,
234234
context->arg_cctx, FALSE, FALSE);
235235
}
@@ -243,7 +243,7 @@ check_arg_type_mod(
243243
type_T *actual,
244244
argcontext_T *context)
245245
{
246-
if (need_type(actual, expected, FALSE,
246+
if (need_type(actual, expected, 0,
247247
context->arg_idx - context->arg_count, context->arg_idx + 1,
248248
context->arg_cctx, FALSE, FALSE) == FAIL)
249249
return FAIL;

src/proto/vim9compile.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack);
66
int cctx_class_method_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
77
int cctx_class_member_idx(cctx_T *cctx, char_u *name, size_t len, class_T **cl_ret);
88
int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg);
9-
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);
10-
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);
9+
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);
10+
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);
1111
lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int assign, type_T *type);
1212
int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx, cstack_T *cstack);
1313
imported_T *find_imported(char_u *name, size_t len, int load);

src/proto/vim9instr.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ int generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic);
1616
int generate_CONCAT(cctx_T *cctx, int count);
1717
int generate_2BOOL(cctx_T *cctx, int invert, int offset);
1818
int generate_COND2BOOL(cctx_T *cctx);
19-
int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int number_ok, int offset, int is_var, int argidx);
19+
int generate_TYPECHECK(cctx_T *cctx, type_T *expected, int typechk_flags, int offset, int is_var, int argidx);
2020
int generate_SETTYPE(cctx_T *cctx, type_T *expected);
2121
int generate_PUSHOBJ(cctx_T *cctx);
2222
int generate_tv_PUSH(cctx_T *cctx, typval_T *tv);

src/structs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,7 @@ struct type_S {
15321532
vartype_T tt_type;
15331533
int8_T tt_argcount; // for func, incl. vararg, -1 for unknown
15341534
int8_T tt_min_argcount; // number of non-optional arguments
1535-
char_u tt_flags; // TTFLAG_ values
1535+
short_u tt_flags; // TTFLAG_ values
15361536
type_T *tt_member; // for list, dict, func return type
15371537
class_T *tt_class; // for class and object
15381538
type_T **tt_args; // func argument types, allocated
@@ -1551,10 +1551,15 @@ typedef struct {
15511551
#define TTFLAG_CONST 0x20 // cannot be changed
15521552
#define TTFLAG_SUPER 0x40 // object from "super".
15531553
#define TTFLAG_GENERIC 0x80 // generic type
1554+
#define TTFLAG_TUPLE_OK 0x100 // tuple can be used for a list
15541555

15551556
#define IS_GENERIC_TYPE(type) \
15561557
((type->tt_flags & TTFLAG_GENERIC) == TTFLAG_GENERIC)
15571558

1559+
// Type check flags
1560+
#define TYPECHK_NUMBER_OK 0x1 // number is accepted for a float
1561+
#define TYPECHK_TUPLE_OK 0x2 // tuple is accepted for a list
1562+
15581563
typedef enum {
15591564
VIM_ACCESS_PRIVATE, // read/write only inside the class
15601565
VIM_ACCESS_READ, // read everywhere, write only inside the class

src/testdir/test_tuple.vim

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,4 +2356,103 @@ func Test_tuple2list()
23562356
call v9.CheckSourceDefAndScriptSuccess(lines)
23572357
endfunc
23582358

2359+
" Test for assigning multiple variables (list assign) using an imported
2360+
" function returning a tuple.
2361+
func Test_tuple_multi_assign_from_import()
2362+
let lines =<< trim END
2363+
vim9script
2364+
2365+
export def Fn(): tuple<string, string>
2366+
return ('aaa', 'bbb')
2367+
enddef
2368+
END
2369+
call writefile(lines, 'Ximporttuplefunc.vim', 'D')
2370+
2371+
let lines =<< trim END
2372+
vim9script
2373+
2374+
import "./Ximporttuplefunc.vim" as Xtuple
2375+
2376+
def XtestTupleListAssign()
2377+
const [x, y] = Xtuple.Fn()
2378+
assert_equal(['aaa', 'bbb'], [x, y])
2379+
enddef
2380+
XtestTupleListAssign()
2381+
2382+
# Check the generated code
2383+
def XtestTupleListAssign2()
2384+
const [x, y] = Xtuple.Fn()
2385+
enddef
2386+
var res = execute('disass XtestTupleListAssign2')
2387+
assert_match('<SNR>\d*_XtestTupleListAssign2\_s*' ..
2388+
'const \[x, y\] = Xtuple.Fn()\_s*' ..
2389+
'0 PUSHFUNC "<80><fd>R\d\+_Fn"\_s*' ..
2390+
'1 PCALL top (argc 0)\_s*' ..
2391+
'2 PCALL end\_s*' ..
2392+
'3 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
2393+
'4 CHECKLEN 2\_s*' ..
2394+
'5 ITEM 0\_s*' ..
2395+
'6 LOCKCONST\_s*' ..
2396+
'7 STORE $0\_s*' ..
2397+
'8 ITEM 1\_s*' ..
2398+
'9 LOCKCONST\_s*' ..
2399+
'10 STORE $1\_s*' ..
2400+
'11 DROP\_s*' ..
2401+
'12 RETURN void',
2402+
res)
2403+
END
2404+
call v9.CheckSourceScriptSuccess(lines)
2405+
endfunc
2406+
2407+
" Test for assigning multiple variables in a for loop using an imported
2408+
" function returning a tuple.
2409+
func Test_tuple_multi_assign_in_for_loop_from_import()
2410+
let lines =<< trim END
2411+
vim9script
2412+
2413+
export def Fn(): tuple<...list<tuple<string, string>>>
2414+
return (('a', 'b'), ('c', 'd'))
2415+
enddef
2416+
END
2417+
call writefile(lines, 'Ximporttupleinfor.vim', 'D')
2418+
2419+
let lines =<< trim END
2420+
vim9script
2421+
2422+
import "./Ximporttupleinfor.vim" as Xtuple
2423+
2424+
def XtestTupleInFor()
2425+
var s = ''
2426+
for [x, y] in Xtuple.Fn()
2427+
s ..= x .. y
2428+
endfor
2429+
assert_equal('abcd', s)
2430+
enddef
2431+
XtestTupleInFor()
2432+
2433+
# Check the generated code
2434+
def XtestTupleInFor2()
2435+
for [x, y] in Xtuple.Fn()
2436+
endfor
2437+
enddef
2438+
var res = execute('disass XtestTupleInFor2')
2439+
assert_match('<SNR>\d*_XtestTupleInFor2\_s*' ..
2440+
'for \[x, y\] in Xtuple.Fn()\_s*' ..
2441+
'0 STORE -1 in $0\_s*' ..
2442+
'1 PUSHFUNC "<80><fd>R\d\+_Fn"\_s*' ..
2443+
'2 PCALL top (argc 0)\_s*' ..
2444+
'3 PCALL end\_s*' ..
2445+
'4 FOR $0 -> 9\_s*' ..
2446+
'5 UNPACK 2\_s*' ..
2447+
'6 STORE $2\_s*' ..
2448+
'7 STORE $3\_s*' ..
2449+
'endfor\_s*' ..
2450+
'8 JUMP -> 4\_s*' ..
2451+
'9 DROP\_s*' ..
2452+
'10 RETURN void',
2453+
res)
2454+
END
2455+
call v9.CheckSourceScriptSuccess(lines)
2456+
endfunc
2457+
23592458
" vim: shiftwidth=2 sts=2 expandtab

src/testdir/test_vim9_disassemble.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ def Test_disassemble_list_assign()
647647
'\d STORE $2\_s*' ..
648648
'\[x, y; l\] = g:stringlist\_s*' ..
649649
'\d LOADG g:stringlist\_s*' ..
650-
'\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
650+
'\d CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
651651
'\d CHECKLEN >= 2\_s*' ..
652652
'\d\+ ITEM 0\_s*' ..
653653
'\d\+ CHECKTYPE string stack\[-1\] var 1\_s*' ..

src/testdir/test_vim9_generics.vim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,7 +2064,7 @@ def Test_generic_function_disassemble()
20642064
'5 STORE $1\_s*' ..
20652065
'\[x, y\] = g:values\_s*' ..
20662066
'6 LOADG g:values\_s*\_s*' ..
2067-
'7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
2067+
'7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
20682068
'8 CHECKLEN 2\_s*' ..
20692069
'9 ITEM 0\_s*' ..
20702070
'10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
@@ -2201,7 +2201,7 @@ def Test_generic_disassemble_generic_obj_method()
22012201
'5 STORE $2\_s*' ..
22022202
'\[x, y\] = g:values\_s*' ..
22032203
'6 LOADG g:values\_s*' ..
2204-
'7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
2204+
'7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
22052205
'8 CHECKLEN 2\_s*' ..
22062206
'9 ITEM 0\_s*' ..
22072207
'10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..
@@ -2329,7 +2329,7 @@ def Test_generic_disassemble_generic_class_method()
23292329
'5 STORE $1\_s*' ..
23302330
'\[x, y\] = g:values\_s*' ..
23312331
'6 LOADG g:values\_s*' ..
2332-
'7 CHECKTYPE list<any> stack\[-1\]\_s*' ..
2332+
'7 CHECKTYPE list<any>|tuple<any> stack\[-1\]\_s*' ..
23332333
'8 CHECKLEN 2\_s*' ..
23342334
'9 ITEM 0\_s*' ..
23352335
'10 CHECKTYPE list<string> stack\[-1\] var 1\_s*' ..

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ static char *(features[]) =
734734

735735
static int included_patches[] =
736736
{ /* Add new patch number below this line */
737+
/**/
738+
2054,
737739
/**/
738740
2053,
739741
/**/

src/vim9cmds.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
11721172
if (lhs_type == &t_any)
11731173
lhs_type = item_type;
11741174
else if (item_type != &t_unknown
1175-
&& need_type_where(item_type, lhs_type, FALSE, -1,
1175+
&& need_type_where(item_type, lhs_type, 0, -1,
11761176
where, cctx, FALSE, FALSE) == FAIL)
11771177
goto failed;
11781178
var_lvar = reserve_local(cctx, arg, varlen, ASSIGN_FINAL,
@@ -2625,7 +2625,7 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
26252625
if (compile_assign_lhs(arg, lhs, CMD_redir,
26262626
FALSE, FALSE, FALSE, 1, cctx) == FAIL)
26272627
return NULL;
2628-
if (need_type(&t_string, lhs->lhs_member_type, FALSE,
2628+
if (need_type(&t_string, lhs->lhs_member_type, 0,
26292629
-1, 0, cctx, FALSE, FALSE) == FAIL)
26302630
return NULL;
26312631
if (cctx->ctx_skip == SKIP_YES)
@@ -2707,7 +2707,7 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx)
27072707
int save_flags = cmdmod.cmod_flags;
27082708

27092709
generate_LEGACY_EVAL(cctx, p);
2710-
if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, FALSE, -1,
2710+
if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, 0, -1,
27112711
0, cctx, FALSE, FALSE) == FAIL)
27122712
return NULL;
27132713
cmdmod.cmod_flags |= CMOD_LEGACY;
@@ -2738,7 +2738,7 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx)
27382738
}
27392739
else
27402740
{
2741-
if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, FALSE,
2741+
if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, 0,
27422742
-1, 0, cctx, FALSE, FALSE) == FAIL)
27432743
return NULL;
27442744
}

src/vim9compile.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -526,12 +526,16 @@ use_typecheck(type_T *actual, type_T *expected)
526526
* - return FAIL.
527527
* If "actual_is_const" is TRUE then the type won't change at runtime, do not
528528
* generate a TYPECHECK.
529+
* If "typechk_flags" has TYPECHK_NUMBER_OK, then a number is accepted for
530+
* a float.
531+
* If "typechk_flags" has TYPECHK_TUPLE_OK, then a tuple is accepted for a
532+
* list.
529533
*/
530534
int
531535
need_type_where(
532536
type_T *actual,
533537
type_T *expected,
534-
int number_ok, // expect VAR_FLOAT but VAR_NUMBER is OK
538+
int typechk_flags, // acceptable types (type check flags)
535539
int offset,
536540
where_T where,
537541
cctx_T *cctx,
@@ -567,7 +571,7 @@ need_type_where(
567571
// If the actual type can be the expected type add a runtime check.
568572
if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected))
569573
{
570-
generate_TYPECHECK(cctx, expected, number_ok, offset,
574+
generate_TYPECHECK(cctx, expected, typechk_flags, offset,
571575
where.wt_kind == WT_VARIABLE, where.wt_index);
572576
return OK;
573577
}
@@ -581,7 +585,7 @@ need_type_where(
581585
need_type(
582586
type_T *actual,
583587
type_T *expected,
584-
int number_ok, // when expected is float number is also OK
588+
int typechk_flags, // acceptable types (type check flags)
585589
int offset,
586590
int arg_idx,
587591
cctx_T *cctx,
@@ -595,7 +599,7 @@ need_type(
595599
where.wt_index = arg_idx;
596600
where.wt_kind = WT_ARGUMENT;
597601
}
598-
return need_type_where(actual, expected, number_ok, offset, where,
602+
return need_type_where(actual, expected, typechk_flags, offset, where,
599603
cctx, silent, actual_is_const);
600604
}
601605

@@ -2521,7 +2525,7 @@ compile_load_lhs(
25212525
if (rhs_type != NULL && member_type != NULL
25222526
&& vartype != VAR_OBJECT && vartype != VAR_CLASS
25232527
&& rhs_type != &t_void
2524-
&& need_type(rhs_type, member_type, FALSE,
2528+
&& need_type(rhs_type, member_type, 0,
25252529
-3, 0, cctx, FALSE, FALSE) == FAIL)
25262530
return FAIL;
25272531

@@ -2680,13 +2684,13 @@ compile_assign_unlet(
26802684
if (range)
26812685
{
26822686
type = get_type_on_stack(cctx, 1);
2683-
if (need_type(type, &t_number, FALSE,
2687+
if (need_type(type, &t_number, 0,
26842688
-2, 0, cctx, FALSE, FALSE) == FAIL)
26852689
return FAIL;
26862690
}
26872691
type = get_type_on_stack(cctx, 0);
26882692
if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL)
2689-
&& need_type(type, &t_number, FALSE,
2693+
&& need_type(type, &t_number, 0,
26902694
-1, 0, cctx, FALSE, FALSE) == FAIL)
26912695
return FAIL;
26922696
}
@@ -3041,7 +3045,7 @@ compile_assign_list_check_rhs_type(cctx_T *cctx, cac_T *cac)
30413045

30423046
if (need_type(stacktype,
30433047
stacktype->tt_type == VAR_TUPLE ? &t_tuple_any : &t_list_any,
3044-
FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
3048+
TYPECHK_TUPLE_OK, -1, 0, cctx, FALSE, FALSE) == FAIL)
30453049
return FAIL;
30463050

30473051
if (stacktype->tt_type == VAR_TUPLE)
@@ -3280,7 +3284,7 @@ compile_assign_valid_rhs_type(
32803284
!has_list_index(cac->cac_var_start + lhs->lhs_varlen, cctx))
32813285
use_type = lhs->lhs_member_type;
32823286

3283-
if (need_type_where(rhs_type, use_type, FALSE, -1, where, cctx, FALSE,
3287+
if (need_type_where(rhs_type, use_type, 0, -1, where, cctx, FALSE,
32843288
cac->cac_is_const) == FAIL)
32853289
return FALSE;
32863290

@@ -3342,7 +3346,7 @@ compile_assign_check_type(cctx_T *cctx, cac_T *cac)
33423346

33433347
if (*cac->cac_nextc != '=')
33443348
{
3345-
if (need_type(rhs_type, lhs_type, FALSE, -1, 0, cctx, FALSE,
3349+
if (need_type(rhs_type, lhs_type, 0, -1, 0, cctx, FALSE,
33463350
FALSE) == FAIL)
33473351
return FAIL;
33483352
}
@@ -3493,7 +3497,7 @@ compile_assign_compound_op(cctx_T *cctx, cac_T *cac)
34933497
expected = lhs->lhs_member_type;
34943498
stacktype = get_type_on_stack(cctx, 0);
34953499
if (expected != &t_string
3496-
&& need_type(stacktype, expected, FALSE, -1, 0, cctx,
3500+
&& need_type(stacktype, expected, 0, -1, 0, cctx,
34973501
FALSE, FALSE) == FAIL)
34983502
return FAIL;
34993503
else if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
@@ -3511,8 +3515,8 @@ compile_assign_compound_op(cctx_T *cctx, cac_T *cac)
35113515
// If variable is float operation with number is OK.
35123516
!(expected == &t_float && (stacktype == &t_number
35133517
|| stacktype == &t_number_bool))
3514-
&& need_type(stacktype, expected, TRUE, -1, 0, cctx,
3515-
FALSE, FALSE) == FAIL)
3518+
&& need_type(stacktype, expected, TYPECHK_NUMBER_OK, -1, 0,
3519+
cctx, FALSE, FALSE) == FAIL)
35163520
return FAIL;
35173521
}
35183522

@@ -3659,7 +3663,7 @@ compile_assign_process_variables(
36593663
SOURCING_LNUM = cac->cac_start_lnum;
36603664
if (cac->cac_lhs.lhs_has_type
36613665
&& need_type(&t_list_string, cac->cac_lhs.lhs_type,
3662-
FALSE, -1, 0, cctx, FALSE, FALSE) == FAIL)
3666+
0, -1, 0, cctx, FALSE, FALSE) == FAIL)
36633667
return FAIL;
36643668
}
36653669
else
@@ -4119,7 +4123,7 @@ obj_constructor_prologue(ufunc_T *ufunc, cctx_T *cctx)
41194123
where_T where = WHERE_INIT;
41204124
where.wt_kind = WT_MEMBER;
41214125
where.wt_func_name = (char *)m->ocm_name.string;
4122-
if (need_type_where(type, m->ocm_type, FALSE, -1,
4126+
if (need_type_where(type, m->ocm_type, 0, -1,
41234127
where, cctx, FALSE, FALSE) == FAIL)
41244128
return FAIL;
41254129
}
@@ -4224,7 +4228,7 @@ compile_def_function_default_args(
42244228
ufunc->uf_arg_types[arg_idx] = val_type;
42254229
}
42264230
else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
4227-
FALSE, -1, where, cctx, FALSE, FALSE) == FAIL)
4231+
0, -1, where, cctx, FALSE, FALSE) == FAIL)
42284232
return FAIL;
42294233

42304234
if (generate_STORE(cctx, ISN_STORE, i - count - off, NULL) == FAIL)

0 commit comments

Comments
 (0)