diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-08-09 19:02:50 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-08-09 19:02:50 +0200 |
commit | 64d662d5fc2ff8af4dbf399ff02aa9d711cc9312 (patch) | |
tree | e10acf8f148ec9bc01c5077bd3573ec4ac10eceb | |
parent | 127542bcebeb6480493b09d75a3be1d98a5f7797 (diff) |
patch 8.2.1408: Vim9: type casting not supportedv8.2.1408
Problem: Vim9: type casting not supported.
Solution: Introduce type casting.
-rw-r--r-- | runtime/doc/vim9.txt | 29 | ||||
-rw-r--r-- | src/testdir/test_vim9_disassemble.vim | 18 | ||||
-rw-r--r-- | src/testdir/test_vim9_expr.vim | 6 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9compile.c | 54 |
5 files changed, 107 insertions, 2 deletions
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 8ff70595f4..7c4a64f972 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -640,6 +640,35 @@ And classes and interfaces can be used as types: > {not implemented yet} +Variable types and type casting *variable-types* + +Variables declared in Vim9 script or in a `:def` function have a type, either +specified explicitly or inferred from the initialization. + +Global, buffer, window and tab page variables do not have a specific type, the +value can be changed at any time, possibly changing the type. Therefore, in +compiled code the "any" type is assumed. + +This can be a problem when the "any" type is undesired and the actual type is +expected to always be the same. For example, when declaring a list: > + let l: list<number> = [1, g:two] +This will give an error, because "g:two" has type "any". To avoid this, use a +type cast: > + let l: list<number> = [1, <number>g:two] +< *type-casting* +The compiled code will then check that "g:two" is a number at runtime and give +an error if it isn't. This is called type casting. + +The syntax of a type cast is: "<" {type} ">". There cannot be white space +after the "<" or before the ">" (to avoid them being confused with +smaller-than and bigger-than operators). + +The semantics is that, if needed, a runtime type check is performed. The +value is not actually changed. If you need to change the type, e.g. to change +it to a string, use the |string()| function. Or use |str2nr()| to convert a +string to a number. + + Type inference *type-inference* In general: Whenever the type is clear it can be omitted. For example, when diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index ea012b744e..5d9447d8ca 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -817,6 +817,24 @@ enddef let g:number = 42 +def TypeCast() + let l: list<number> = [23, <number>g:number] +enddef + +def Test_disassemble_typecast() + let instr = execute('disassemble TypeCast') + assert_match('TypeCast.*' .. + 'let l: list<number> = \[23, <number>g:number\].*' .. + '\d PUSHNR 23\_s*' .. + '\d LOADG g:number\_s*' .. + '\d CHECKTYPE number stack\[-1\]\_s*' .. + '\d NEWLIST size 2\_s*' .. + '\d STORE $0\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d RETURN\_s*', + instr) +enddef + def Computing() let nr = 3 let nrres = nr + 7 diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 7891127e5c..0af5402cc2 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1247,6 +1247,12 @@ let g:dict_one = #{one: 1} let $TESTVAR = 'testvar' +" type casts +def Test_expr7t() + let ls: list<string> = ['a', <string>g:string_empty] + let ln: list<number> = [<number>g:anint, <number>g:alsoint] +enddef + " test low level expression def Test_expr7_number() # number constant diff --git a/src/version.c b/src/version.c index e7dd7dd0f1..b7667bf4c6 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1408, +/**/ 1407, /**/ 1406, diff --git a/src/vim9compile.c b/src/vim9compile.c index 19760fb695..81206b3aca 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3402,6 +3402,56 @@ error_white_both(char_u *op, int len) } /* + * <type>expr7: runtime type check / conversion + */ + static int +compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) +{ + type_T *want_type = NULL; + + // Recognize <type> + if (**arg == '<' && eval_isnamec1((*arg)[1])) + { + int called_emsg_before = called_emsg; + + ++*arg; + want_type = parse_type(arg, cctx->ctx_type_list); + if (called_emsg != called_emsg_before) + return FAIL; + + if (**arg != '>') + { + if (*skipwhite(*arg) == '>') + semsg(_(e_no_white_before), ">"); + else + emsg(_("E1104: Missing >")); + return FAIL; + } + ++*arg; + if (may_get_next_line_error(*arg - 1, arg, cctx) == FAIL) + return FAIL; + } + + if (compile_expr7(arg, cctx, ppconst) == FAIL) + return FAIL; + + if (want_type != NULL) + { + garray_T *stack = &cctx->ctx_type_stack; + type_T *actual = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + + if (check_type(want_type, actual, FALSE) == FAIL) + { + generate_ppconst(cctx, ppconst); + if (need_type(actual, want_type, -1, cctx, FALSE) == FAIL) + return FAIL; + } + } + + return OK; +} + +/* * * number multiplication * / number division * % number modulo @@ -3414,7 +3464,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) int ppconst_used = ppconst->pp_used; // get the first expression - if (compile_expr7(arg, cctx, ppconst) == FAIL) + if (compile_expr7t(arg, cctx, ppconst) == FAIL) return FAIL; /* @@ -3441,7 +3491,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) return FAIL; // get the second expression - if (compile_expr7(arg, cctx, ppconst) == FAIL) + if (compile_expr7t(arg, cctx, ppconst) == FAIL) return FAIL; if (ppconst->pp_used == ppconst_used + 2 |