summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-08-09 19:02:50 +0200
committerBram Moolenaar <Bram@vim.org>2020-08-09 19:02:50 +0200
commit64d662d5fc2ff8af4dbf399ff02aa9d711cc9312 (patch)
treee10acf8f148ec9bc01c5077bd3573ec4ac10eceb
parent127542bcebeb6480493b09d75a3be1d98a5f7797 (diff)
downloadvim-git-64d662d5fc2ff8af4dbf399ff02aa9d711cc9312.tar.gz
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.txt29
-rw-r--r--src/testdir/test_vim9_disassemble.vim18
-rw-r--r--src/testdir/test_vim9_expr.vim6
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c54
5 files changed, 107 insertions, 2 deletions
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index 8ff70595f..7c4a64f97 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 ea012b744..5d9447d8c 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 7891127e5..0af5402cc 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 e7dd7dd0f..b7667bf4c 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 19760fb69..81206b3ac 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