]> granicus.if.org Git - vim/commitdiff
patch 8.2.1408: Vim9: type casting not supported v8.2.1408
authorBram Moolenaar <Bram@vim.org>
Sun, 9 Aug 2020 17:02:50 +0000 (19:02 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 9 Aug 2020 17:02:50 +0000 (19:02 +0200)
Problem:    Vim9: type casting not supported.
Solution:   Introduce type casting.

runtime/doc/vim9.txt
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9compile.c

index 8ff70595f464839e98aa88eb1ae5c25b3701734a..7c4a64f972dc4169162fe44b03ec5863ac53ebe0 100644 (file)
@@ -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
index ea012b744e98424c4010b6d9421c9c919c9f091c..5d9447d8ca11a5c16a9904d46a5ccd7210d1a48c 100644 (file)
@@ -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
index 7891127e5cd87442fbd401830220ccc3057b03ff..0af5402cc280f4f65dc4c07e407ed0941b716481 100644 (file)
@@ -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
index e7dd7dd0f1121cf544ff7767607ded250ab62a0c..b7667bf4c6bb504b15460e800264baf68f437574 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1408,
 /**/
     1407,
 /**/
index 19760fb695f63e590201f3bf726ef8efff13024b..81206b3aca1b0ea25bfbf7f356938c4ba6063d17 100644 (file)
@@ -3401,6 +3401,56 @@ error_white_both(char_u *op, int len)
     semsg(_(e_white_both), buf);
 }
 
+/*
+ * <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
@@ -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