patch 9.1.1274: Vim9: no support for object<type> as variable type
Problem: Vim9: no support for object<type> as variable type
Solution: add support for object<type> (Yegappan Lakshmanan)
closes: #17041
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index c6d93b2..4b6cb81 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -12808,4 +12808,178 @@
v9.CheckSourceSuccess(lines)
enddef
+" Test for using the type() and typename() functions with a variable of type
+" object
+def Test_type_typename_funcs_with_object_variable()
+ var lines =<< trim END
+ vim9script
+
+ class A
+ endclass
+
+ class B
+ endclass
+
+ var o1: object<any>
+ assert_equal([13, 'object<any>'], [type(o1), typename(o1)])
+
+ var o2: object<A>
+ assert_equal([13, 'object<any>'], [type(o2), typename(o2)])
+
+ var o3: A
+ assert_equal([13, 'object<any>'], [type(o3), typename(o3)])
+
+ var o4 = A.new()
+ assert_equal([13, 'object<A>'], [type(o4), typename(o4)])
+
+ var l = [A.new(), B.new()]
+ assert_equal([13, 'object<B>'], [type(l[1]), typename(l[1])])
+
+ var d = {o_a: A.new(), o_b: B.new()}
+ assert_equal([13, 'object<B>'], [type(d.o_b), typename(d.o_b)])
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for object<any> type
+def Test_object_any_type()
+ # assigning different objects to variable of type object<any>
+ var lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B
+ endclass
+ var x: object<any>
+ x = A.new()
+ assert_true(instanceof(x, A))
+ x = B.new()
+ assert_true(instanceof(x, B))
+ x = null_object
+ assert_true(instanceof(x, null_class))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use a list of object<any> variable
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B
+ endclass
+ var l: list<object<any>>
+ l->add(A.new())
+ l->add(B.new())
+ assert_true(instanceof(l[0], A))
+ assert_true(instanceof(l[1], B))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using object<any> as a function argument type and the return type
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B
+ endclass
+ def Fn(x: object<any>): object<any>
+ return x
+ enddef
+ assert_true(instanceof(Fn(A.new()), A))
+ assert_true(instanceof(Fn(B.new()), B))
+ END
+
+ # Try assigning a different type of value to a object<any> variable
+ lines =<< trim END
+ var x: object<any> = []
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got list<any>', 'E1012: Type mismatch; expected object<any> but got list<any>'])
+
+ # Try assigning a different type of value to a object<any> variable
+ lines =<< trim END
+ var x: object<any>
+ x = 0z10
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got blob', 'E1012: Type mismatch; expected object<any> but got blob'])
+
+ # Try adding a different type of value to a list<object<any>> variable
+ lines =<< trim END
+ var x: list<object<any>>
+ x->add({})
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got dict<any>', 'E1012: Type mismatch; expected object<any> but got dict<any>'])
+
+ # Try adding a different type of value to a dict<object<any>> variable
+ lines =<< trim END
+ var x: dict<object<any>>
+ x['a'] = {}
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object<any> but got dict<any>', 'E1012: Type mismatch; expected object<any> but got dict<any>'])
+enddef
+
+" Test for object<{class}> type
+def Test_object_of_class_type()
+ var lines =<< trim END
+ vim9script
+ class A
+ endclass
+ var x: object<A>
+ x = A.new()
+ assert_true(instanceof(x, A))
+ var y: object<A> = A.new()
+ assert_true(instanceof(y, A))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B
+ endclass
+ var x: object<A>
+ x = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<A> but got object<B>')
+
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B
+ endclass
+ def Fn(x: object<A>): object<B>
+ return B.new()
+ enddef
+ assert_true(instanceof(Fn(A.new()), B))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ var x: object
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1008: Missing <type> after object', 'E1008: Missing <type> after object'])
+
+ lines =<< trim END
+ var x: object <any>
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1068: No white space allowed before ''<'': <any>', 'E1068: No white space allowed before ''<'': <any>'])
+
+ lines =<< trim END
+ var x: object<any
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, ['E1009: Missing > after type: <any', 'E1009: Missing > after type: <any'])
+
+ lines =<< trim END
+ var x: object<any,any>
+ END
+ v9.CheckSourceDefFailure(lines, 'E1009: Missing > after type: <any,any>')
+
+ lines =<< trim END
+ vim9script
+ var x: object<any,any>
+ END
+ v9.CheckSourceFailure(lines, 'E488: Trailing characters: ,any>')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index e00e007..42576d1 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1274,
+/**/
1273,
/**/
1272,
diff --git a/src/vim9class.c b/src/vim9class.c
index 5249f40..560c5ae 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -4132,7 +4132,12 @@
return;
if (object_tv->vval.v_object == NULL)
+ {
+ if (classinfo_tv->vval.v_class == NULL)
+ // consider null_object as an instance of null_class
+ rettv->vval.v_number = VVAL_TRUE;
return;
+ }
for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
{
diff --git a/src/vim9type.c b/src/vim9type.c
index 2f85095..a3881c9 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -1785,6 +1785,63 @@
}
/*
+ * Parse a "object" type at "*arg" and advance over it.
+ * When "give_error" is TRUE give error messages, otherwise be quiet.
+ * Return NULL for failure.
+ */
+ static type_T *
+parse_type_object(char_u **arg, garray_T *type_gap, int give_error)
+{
+ char_u *arg_start = *arg;
+ type_T *object_type;
+ int prev_called_emsg = called_emsg;
+
+ // object<X> or object<any>
+ if (**arg != '<')
+ {
+ if (give_error)
+ {
+ if (*skipwhite(*arg) == '<')
+ semsg(_(e_no_white_space_allowed_before_str_str), "<", *arg);
+ else
+ semsg(_(e_missing_type_after_str), "object");
+ }
+
+ // only "object" is specified
+ return NULL;
+ }
+
+ // skip spaces following "object<"
+ *arg = skipwhite(*arg + 1);
+
+ object_type = parse_type(arg, type_gap, give_error);
+ if (object_type == NULL)
+ return NULL;
+
+ *arg = skipwhite(*arg);
+ if (**arg != '>' && called_emsg == prev_called_emsg)
+ {
+ if (give_error)
+ semsg(_(e_missing_gt_after_type_str), arg_start);
+ return NULL;
+ }
+ ++*arg;
+
+ if (object_type->tt_type == VAR_ANY)
+ return &t_object_any;
+
+ if (object_type->tt_type != VAR_OBJECT)
+ {
+ // specified type is not a class
+ if (give_error)
+ semsg(_(e_class_name_not_found_str), arg_start);
+ return NULL;
+ }
+
+ return object_type;
+}
+
+/*
* Parse a user defined type at "*arg" and advance over it.
* It can be a class or an interface or a typealias name, possibly imported.
* Return NULL if a type is not found.
@@ -1932,6 +1989,13 @@
return &t_number;
}
break;
+ case 'o':
+ if (len == 6 && STRNCMP(*arg, "object", len) == 0)
+ {
+ *arg += len;
+ return parse_type_object(arg, type_gap, give_error);
+ }
+ break;
case 's':
if (len == 6 && STRNCMP(*arg, "string", len) == 0)
{