]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.3844: Vim9: no type error if assigning func(number) to func(string) v8.2.3844
authorBram Moolenaar <Bram@vim.org>
Sat, 18 Dec 2021 12:31:33 +0000 (12:31 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 18 Dec 2021 12:31:33 +0000 (12:31 +0000)
Problem:    Vim9: no type error if assigning a value with type func(number) to
            a variable of type func(string).
Solution:   Use check_type_maybe(): return MAYBE if a runtime type check is
            useful.  (issue #8492)

src/proto/vim9type.pro
src/testdir/test_vim9_assign.vim
src/version.c
src/vim9compile.c
src/vim9type.c

index b14e46665bb423ac28b889a2e7c5d83885d96529..0bdc2277454955cb45c49ec503dfd79a7bec0fe5 100644 (file)
@@ -16,6 +16,7 @@ void type_mismatch(type_T *expected, type_T *actual);
 void arg_type_mismatch(type_T *expected, type_T *actual, int arg_idx);
 void type_mismatch_where(type_T *expected, type_T *actual, where_T where);
 int check_type(type_T *expected, type_T *actual, int give_msg, where_T where);
+int check_type_maybe(type_T *expected, type_T *actual, int give_msg, where_T where);
 int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
 char_u *skip_type(char_u *start, int optional);
 type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
index e1fe095784149d6d1711d49b160706fe22839bd0..f7af299e426d34e65330b762f738af9a0ba09c74 100644 (file)
@@ -2146,6 +2146,23 @@ def Test_script_funcref_case()
   CheckScriptFailure(lines, 'E704:')
 enddef
 
+def Test_script_funcref_runtime_type_check()
+  var lines =<< trim END
+      vim9script
+      def FuncWithNumberArg(n: number)
+      enddef
+      def Test()
+        var Ref: func(string) = function(FuncWithNumberArg)
+      enddef
+      defcompile
+  END
+  # OK at compile time
+  CheckScriptSuccess(lines)
+
+  # Type check fails at runtime
+  CheckScriptFailure(lines + ['Test()'], 'E1012: Type mismatch; expected func(string) but got func(number)')
+enddef
+
 def Test_inc_dec()
   var lines =<< trim END
       var nr = 7
index 0e2a2298f324bc672de8553ad6c30d5d5d04a1d9..dbf2fe5ac67760f88b809bdeed1f597a9fe3ecdc 100644 (file)
@@ -749,6 +749,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3844,
 /**/
     3843,
 /**/
index 388308abfacaedb2408a7c294dba3ec980d8357d..d6fc6d85810202b89f9572b2914af619837f4799 100644 (file)
@@ -1061,6 +1061,8 @@ need_type_where(
        int     silent,
        int     actual_is_const)
 {
+    int ret;
+
     if (expected == &t_bool && actual != &t_bool
                                        && (actual->tt_flags & TTFLAG_BOOL_OK))
     {
@@ -1070,13 +1072,14 @@ need_type_where(
        return OK;
     }
 
-    if (check_type(expected, actual, FALSE, where) == OK)
+    ret = check_type_maybe(expected, actual, FALSE, where);
+    if (ret == OK)
        return OK;
 
     // 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 ((!actual_is_const || actual == &t_any)
-                                           && use_typecheck(actual, expected))
+    if (ret == MAYBE || ((!actual_is_const || actual == &t_any)
+                                          && use_typecheck(actual, expected)))
     {
        generate_TYPECHECK(cctx, expected, offset, where.wt_index);
        return OK;
index db78669f75e5d6f2d4cdd80249f32a5d8b3eb661..f8b1272f170f140723dfd93f30312250009d1b67 100644 (file)
@@ -531,9 +531,31 @@ type_mismatch_where(type_T *expected, type_T *actual, where_T where)
  * Check if the expected and actual types match.
  * Does not allow for assigning "any" to a specific type.
  * When "argidx" > 0 it is included in the error message.
+ * Return OK if types match.
+ * Return FAIL if types do not match.
  */
     int
-check_type(type_T *expected, type_T *actual, int give_msg, where_T where)
+check_type(
+       type_T  *expected,
+       type_T  *actual,
+       int     give_msg,
+       where_T where)
+{
+    int ret = check_type_maybe(expected, actual, give_msg, where);
+
+    return ret == MAYBE ? OK : ret;
+}
+
+/*
+ * As check_type() but return MAYBE when a runtime type check should be used
+ * when compiling.
+ */
+    int
+check_type_maybe(
+       type_T  *expected,
+       type_T  *actual,
+       int     give_msg,
+       where_T where)
 {
     int ret = OK;
 
@@ -568,17 +590,21 @@ check_type(type_T *expected, type_T *actual, int give_msg, where_T where)
        {
            // If the return type is unknown it can be anything, including
            // nothing, thus there is no point in checking.
-           if (expected->tt_member != &t_unknown
-                                           && actual->tt_member != &t_unknown)
-               ret = check_type(expected->tt_member, actual->tt_member,
+           if (expected->tt_member != &t_unknown)
+           {
+               if (actual->tt_member != &t_unknown)
+                   ret = check_type(expected->tt_member, actual->tt_member,
                                                                 FALSE, where);
-           if (ret == OK && expected->tt_argcount != -1
+               else
+                   ret = MAYBE;
+           }
+           if (ret != FAIL && expected->tt_argcount != -1
                    && actual->tt_min_argcount != -1
                    && (actual->tt_argcount == -1
                        || (actual->tt_argcount < expected->tt_min_argcount
                            || actual->tt_argcount > expected->tt_argcount)))
                ret = FAIL;
-           if (ret == OK && expected->tt_args != NULL
+           if (ret != FAIL && expected->tt_args != NULL
                                                    && actual->tt_args != NULL)
            {
                int i;
@@ -593,10 +619,21 @@ check_type(type_T *expected, type_T *actual, int give_msg, where_T where)
                        break;
                    }
            }
+           if (ret == OK && expected->tt_argcount >= 0
+                                                 && actual->tt_argcount == -1)
+               // check the argument count at runtime
+               ret = MAYBE;
        }
        if (ret == FAIL && give_msg)
            type_mismatch_where(expected, actual, where);
     }
+
+    if (ret == OK && expected->tt_type != VAR_UNKNOWN
+           && expected->tt_type != VAR_ANY
+           && (actual->tt_type == VAR_UNKNOWN || actual->tt_type == VAR_ANY))
+       // check the type at runtime
+       ret = MAYBE;
+
     return ret;
 }