summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-12-18 12:31:33 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-18 12:31:33 +0000
commit44a8977de467241a2f9959253d06eff53a8baad9 (patch)
tree4ec6b318b1235e40aaf2fc7989142b0e4a7edca5
parent647ab4cede4dbf412d24748f8e0a64d1cb9239f4 (diff)
downloadvim-git-8.2.3844.tar.gz
patch 8.2.3844: Vim9: no type error if assigning func(number) to func(string)v8.2.3844
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)
-rw-r--r--src/proto/vim9type.pro1
-rw-r--r--src/testdir/test_vim9_assign.vim17
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c9
-rw-r--r--src/vim9type.c49
5 files changed, 69 insertions, 9 deletions
diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro
index b14e46665..0bdc22774 100644
--- a/src/proto/vim9type.pro
+++ b/src/proto/vim9type.pro
@@ -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);
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index e1fe09578..f7af299e4 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index 0e2a2298f..dbf2fe5ac 100644
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3844,
+/**/
3843,
/**/
3842,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 388308abf..d6fc6d858 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -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;
diff --git a/src/vim9type.c b/src/vim9type.c
index db78669f7..f8b1272f1 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -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;
}