]> granicus.if.org Git - vim/commitdiff
patch 8.2.3869: Vim9: type checking for "any" is inconsistent v8.2.3869
authorBram Moolenaar <Bram@vim.org>
Wed, 22 Dec 2021 13:18:39 +0000 (13:18 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 22 Dec 2021 13:18:39 +0000 (13:18 +0000)
Problem:    Vim9: type checking for "any" is inconsistent.
Solution:   Always use a runtime type check for using "any" for a more
            specific type.

src/testdir/test_vim9_func.vim
src/version.c
src/vim9compile.c
src/vim9expr.c
src/vim9type.c

index 81fa7e49c7b94916c71e1deb8c2bfdb8e7002772..935079f7ae35a62e8ca43a20e20ff9d1b0eedfbf 100644 (file)
@@ -548,7 +548,7 @@ def Test_call_default_args()
   END
   CheckScriptFailure(lines, 'E1001: Variable not found: b')
 
-  # using script variable requires matching type or type cast
+  # using script variable requires matching type or type cast when executed
   lines =<< trim END
       vim9script
       var a: any
@@ -557,18 +557,8 @@ def Test_call_default_args()
       enddef
       defcompile
   END
-  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got any')
-
-  lines =<< trim END
-      vim9script
-      var a: any
-      def Func(arg: string = <string>a)
-        echo arg
-      enddef
-      a = 'works'
-      Func()
-  END
-  CheckScriptSuccess(lines)
+  CheckScriptSuccess(lines + ['a = "text"', 'Func()'])
+  CheckScriptFailure(lines + ['a = 123', 'Func()'], 'E1013: Argument 1: type mismatch, expected string but got number')
 
   # using global variable does not require type cast
   lines =<< trim END
index 782267f4be2b805df59ee6b54cbc652d75fcc9bd..c5877df998baff3a998c500cfeaf0d335c1b8586 100644 (file)
@@ -749,6 +749,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3869,
 /**/
     3868,
 /**/
index 50fe7e257ba700c36b6cb20b299976e41b37c608..7565cfb01c16da8376ee524334ba270b0e4f8a27 100644 (file)
@@ -366,8 +366,11 @@ use_typecheck(type_T *actual, type_T *expected)
            || (actual->tt_type == VAR_FUNC
                && (expected->tt_type == VAR_FUNC
                                           || expected->tt_type == VAR_PARTIAL)
-               && (actual->tt_member == &t_any || actual->tt_argcount < 0)
-               && ((actual->tt_member == &t_void)
+               && (actual->tt_member == &t_any
+                   || actual->tt_member == &t_unknown
+                   || actual->tt_argcount < 0)
+               && (actual->tt_member == &t_unknown ||
+                   (actual->tt_member == &t_void)
                                         == (expected->tt_member == &t_void))))
        return TRUE;
     if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT)
@@ -412,8 +415,7 @@ need_type_where(
 
     // If the actual type can be the expected type add a runtime check.
     // If it's a constant a runtime check makes no sense.
-    if (ret == MAYBE || ((!actual_is_const || actual == &t_any)
-                                          && use_typecheck(actual, expected)))
+    if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected))
     {
        generate_TYPECHECK(cctx, expected, offset, where.wt_index);
        return OK;
@@ -2547,8 +2549,8 @@ compile_def_function(
                did_set_arg_type = TRUE;
                ufunc->uf_arg_types[arg_idx] = val_type;
            }
-           else if (check_type(ufunc->uf_arg_types[arg_idx], val_type,
-                                                         TRUE, where) == FAIL)
+           else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
+                                      -1, where, &cctx, FALSE, FALSE) == FAIL)
                goto erret;
 
            if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
index 6fe8e2af1ff044c7f1cb5a1d537029c3b1315a0e..6a1faf839fad60ff41652b31cf820c04bac0b7c1 100644 (file)
@@ -415,7 +415,7 @@ compile_load(
                // Global, Buffer-local, Window-local and Tabpage-local
                // variables can be defined later, thus we don't check if it
                // exists, give an error at runtime.
-               res = generate_LOAD(cctx, isn_type, 0, name, &t_unknown);
+               res = generate_LOAD(cctx, isn_type, 0, name, &t_any);
            }
        }
     }
@@ -2846,6 +2846,7 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            type_T      **typep;
 
            generate_ppconst(cctx, ppconst);
+           ppconst->pp_is_const = FALSE;
 
            // If the types differ, the result has a more generic type.
            typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
index 438c5119782f1c8f0cdac14c7241ba92c709082b..4542e1e081ccffdbb60fa29e89891e4757b29a9f 100644 (file)
@@ -477,7 +477,19 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
     ga_init2(&type_list, sizeof(type_T *), 10);
     actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE);
     if (actual_type != NULL)
-       res = check_type(expected, actual_type, TRUE, where);
+    {
+       res = check_type_maybe(expected, actual_type, TRUE, where);
+       if (res == MAYBE && !(actual_type->tt_type == VAR_FUNC
+                                     && actual_type->tt_member == &t_unknown))
+       {
+           // If a type check is needed that means assigning "any" or
+           // "unknown" to a more specific type, which fails here.
+           // Execpt when it looks like a lambda, since they have an
+           // incomplete type.
+           type_mismatch_where(expected, actual_type, where);
+           res = FAIL;
+       }
+    }
     clear_type_list(&type_list);
     return res;
 }
@@ -567,9 +579,11 @@ check_type_maybe(
     {
        // tt_type should match, except that a "partial" can be assigned to a
        // variable with type "func".
-       // And "unknown" (using global variable) needs a runtime type check.
+       // And "unknown" (using global variable) and "any" need a runtime type
+       // check.
        if (!(expected->tt_type == actual->tt_type
                    || actual->tt_type == VAR_UNKNOWN
+                   || actual->tt_type == VAR_ANY
                    || (expected->tt_type == VAR_FUNC
                                           && actual->tt_type == VAR_PARTIAL)))
        {
@@ -585,10 +599,10 @@ check_type_maybe(
        {
            // "unknown" is used for an empty list or dict
            if (actual->tt_member != NULL && actual->tt_member != &t_unknown)
-               ret = check_type(expected->tt_member, actual->tt_member,
+               ret = check_type_maybe(expected->tt_member, actual->tt_member,
                                                                 FALSE, where);
        }
-       else if (expected->tt_type == VAR_FUNC)
+       else if (expected->tt_type == VAR_FUNC && actual != &t_any)
        {
            // If the return type is unknown it can be anything, including
            // nothing, thus there is no point in checking.
@@ -596,8 +610,8 @@ check_type_maybe(
            {
                if (actual->tt_member != NULL
                                            && actual->tt_member != &t_unknown)
-                   ret = check_type(expected->tt_member, actual->tt_member,
-                                                                FALSE, where);
+                   ret = check_type_maybe(expected->tt_member,
+                                             actual->tt_member, FALSE, where);
                else
                    ret = MAYBE;
            }