Skip to content

Commit 64d662d

Browse files
committed
patch 8.2.1408: Vim9: type casting not supported
Problem: Vim9: type casting not supported. Solution: Introduce type casting.
1 parent 127542b commit 64d662d

5 files changed

Lines changed: 107 additions & 2 deletions

File tree

runtime/doc/vim9.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,35 @@ And classes and interfaces can be used as types: >
640640
{not implemented yet}
641641

642642

643+
Variable types and type casting *variable-types*
644+
645+
Variables declared in Vim9 script or in a `:def` function have a type, either
646+
specified explicitly or inferred from the initialization.
647+
648+
Global, buffer, window and tab page variables do not have a specific type, the
649+
value can be changed at any time, possibly changing the type. Therefore, in
650+
compiled code the "any" type is assumed.
651+
652+
This can be a problem when the "any" type is undesired and the actual type is
653+
expected to always be the same. For example, when declaring a list: >
654+
let l: list<number> = [1, g:two]
655+
This will give an error, because "g:two" has type "any". To avoid this, use a
656+
type cast: >
657+
let l: list<number> = [1, <number>g:two]
658+
< *type-casting*
659+
The compiled code will then check that "g:two" is a number at runtime and give
660+
an error if it isn't. This is called type casting.
661+
662+
The syntax of a type cast is: "<" {type} ">". There cannot be white space
663+
after the "<" or before the ">" (to avoid them being confused with
664+
smaller-than and bigger-than operators).
665+
666+
The semantics is that, if needed, a runtime type check is performed. The
667+
value is not actually changed. If you need to change the type, e.g. to change
668+
it to a string, use the |string()| function. Or use |str2nr()| to convert a
669+
string to a number.
670+
671+
643672
Type inference *type-inference*
644673

645674
In general: Whenever the type is clear it can be omitted. For example, when

src/testdir/test_vim9_disassemble.vim

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,24 @@ enddef
817817

818818
let g:number = 42
819819

820+
def TypeCast()
821+
let l: list<number> = [23, <number>g:number]
822+
enddef
823+
824+
def Test_disassemble_typecast()
825+
let instr = execute('disassemble TypeCast')
826+
assert_match('TypeCast.*' ..
827+
'let l: list<number> = \[23, <number>g:number\].*' ..
828+
'\d PUSHNR 23\_s*' ..
829+
'\d LOADG g:number\_s*' ..
830+
'\d CHECKTYPE number stack\[-1\]\_s*' ..
831+
'\d NEWLIST size 2\_s*' ..
832+
'\d STORE $0\_s*' ..
833+
'\d PUSHNR 0\_s*' ..
834+
'\d RETURN\_s*',
835+
instr)
836+
enddef
837+
820838
def Computing()
821839
let nr = 3
822840
let nrres = nr + 7

src/testdir/test_vim9_expr.vim

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,12 @@ let g:dict_one = #{one: 1}
12471247

12481248
let $TESTVAR = 'testvar'
12491249

1250+
" type casts
1251+
def Test_expr7t()
1252+
let ls: list<string> = ['a', <string>g:string_empty]
1253+
let ln: list<number> = [<number>g:anint, <number>g:alsoint]
1254+
enddef
1255+
12501256
" test low level expression
12511257
def Test_expr7_number()
12521258
# number constant

src/version.c

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

755755
static int included_patches[] =
756756
{ /* Add new patch number below this line */
757+
/**/
758+
1408,
757759
/**/
758760
1407,
759761
/**/

src/vim9compile.c

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,56 @@ error_white_both(char_u *op, int len)
34013401
semsg(_(e_white_both), buf);
34023402
}
34033403

3404+
/*
3405+
* <type>expr7: runtime type check / conversion
3406+
*/
3407+
static int
3408+
compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
3409+
{
3410+
type_T *want_type = NULL;
3411+
3412+
// Recognize <type>
3413+
if (**arg == '<' && eval_isnamec1((*arg)[1]))
3414+
{
3415+
int called_emsg_before = called_emsg;
3416+
3417+
++*arg;
3418+
want_type = parse_type(arg, cctx->ctx_type_list);
3419+
if (called_emsg != called_emsg_before)
3420+
return FAIL;
3421+
3422+
if (**arg != '>')
3423+
{
3424+
if (*skipwhite(*arg) == '>')
3425+
semsg(_(e_no_white_before), ">");
3426+
else
3427+
emsg(_("E1104: Missing >"));
3428+
return FAIL;
3429+
}
3430+
++*arg;
3431+
if (may_get_next_line_error(*arg - 1, arg, cctx) == FAIL)
3432+
return FAIL;
3433+
}
3434+
3435+
if (compile_expr7(arg, cctx, ppconst) == FAIL)
3436+
return FAIL;
3437+
3438+
if (want_type != NULL)
3439+
{
3440+
garray_T *stack = &cctx->ctx_type_stack;
3441+
type_T *actual = ((type_T **)stack->ga_data)[stack->ga_len - 1];
3442+
3443+
if (check_type(want_type, actual, FALSE) == FAIL)
3444+
{
3445+
generate_ppconst(cctx, ppconst);
3446+
if (need_type(actual, want_type, -1, cctx, FALSE) == FAIL)
3447+
return FAIL;
3448+
}
3449+
}
3450+
3451+
return OK;
3452+
}
3453+
34043454
/*
34053455
* * number multiplication
34063456
* / number division
@@ -3414,7 +3464,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
34143464
int ppconst_used = ppconst->pp_used;
34153465

34163466
// get the first expression
3417-
if (compile_expr7(arg, cctx, ppconst) == FAIL)
3467+
if (compile_expr7t(arg, cctx, ppconst) == FAIL)
34183468
return FAIL;
34193469

34203470
/*
@@ -3441,7 +3491,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
34413491
return FAIL;
34423492

34433493
// get the second expression
3444-
if (compile_expr7(arg, cctx, ppconst) == FAIL)
3494+
if (compile_expr7t(arg, cctx, ppconst) == FAIL)
34453495
return FAIL;
34463496

34473497
if (ppconst->pp_used == ppconst_used + 2

0 commit comments

Comments
 (0)