diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 0e9a5080fa..b7b2bcc5fd 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.1. Last change: 2025 Mar 27 +*version9.txt* For Vim version 9.1. Last change: 2025 Apr 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41553,6 +41553,8 @@ Enum support for Vim9 script |:enum| Support for protected _new() method +Add support for object<{type}> as variable data type |vim9-types| + Diff mode ~ --------- Include the "linematch" algorithm for the 'diffopt' setting. This aligns diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index d06c250a3f..54fb8f333e 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.1. Last change: 2025 Mar 23 +*vim9.txt* For Vim version 9.1. Last change: 2025 Apr 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1467,6 +1467,7 @@ The following builtin types are supported: blob list<{type}> dict<{type}> + object<{type}> job channel tuple<{type}> diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index c6d93b2b70..4b6cb81bb6 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -12808,4 +12808,178 @@ def Test_dict_of_objects() 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 + assert_equal([13, 'object'], [type(o1), typename(o1)]) + + var o2: object + assert_equal([13, 'object'], [type(o2), typename(o2)]) + + var o3: A + assert_equal([13, 'object'], [type(o3), typename(o3)]) + + var o4 = A.new() + assert_equal([13, 'object'], [type(o4), typename(o4)]) + + var l = [A.new(), B.new()] + assert_equal([13, 'object'], [type(l[1]), typename(l[1])]) + + var d = {o_a: A.new(), o_b: B.new()} + assert_equal([13, 'object'], [type(d.o_b), typename(d.o_b)]) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for object type +def Test_object_any_type() + # assigning different objects to variable of type object + var lines =<< trim END + vim9script + class A + endclass + class B + endclass + var x: object + 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 variable + lines =<< trim END + vim9script + class A + endclass + class B + endclass + var l: list> + 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 as a function argument type and the return type + lines =<< trim END + vim9script + class A + endclass + class B + endclass + def Fn(x: object): object + 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 variable + lines =<< trim END + var x: object = [] + END + v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object but got list', 'E1012: Type mismatch; expected object but got list']) + + # Try assigning a different type of value to a object variable + lines =<< trim END + var x: object + x = 0z10 + END + v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object but got blob', 'E1012: Type mismatch; expected object but got blob']) + + # Try adding a different type of value to a list> variable + lines =<< trim END + var x: list> + x->add({}) + END + v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object but got dict', 'E1012: Type mismatch; expected object but got dict']) + + # Try adding a different type of value to a dict> variable + lines =<< trim END + var x: dict> + x['a'] = {} + END + v9.CheckSourceDefAndScriptFailure(lines, ['E1012: Type mismatch; expected object but got dict', 'E1012: Type mismatch; expected object but got dict']) +enddef + +" Test for object<{class}> type +def Test_object_of_class_type() + var lines =<< trim END + vim9script + class A + endclass + var x: object + x = A.new() + assert_true(instanceof(x, A)) + var y: object = A.new() + assert_true(instanceof(y, A)) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class A + endclass + class B + endclass + var x: object + x = B.new() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got object') + + lines =<< trim END + vim9script + class A + endclass + class B + endclass + def Fn(x: object): object + 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 after object', 'E1008: Missing after object']) + + lines =<< trim END + var x: object + END + v9.CheckSourceDefAndScriptFailure(lines, ['E1068: No white space allowed before ''<'': ', 'E1068: No white space allowed before ''<'': ']) + + lines =<< trim END + var x: object after type: after type: + END + v9.CheckSourceDefFailure(lines, 'E1009: Missing > after type: ') + + lines =<< trim END + vim9script + var x: object + 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 e00e0077c0..42576d1e8a 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1274, /**/ 1273, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 5249f40de4..560c5ae283 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -4132,7 +4132,12 @@ f_instanceof(typval_T *argvars, typval_T *rettv) 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 2f85095879..a3881c9b1e 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -1784,6 +1784,63 @@ on_err: return ret_type; } +/* + * 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 or object + 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. @@ -1932,6 +1989,13 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error) 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) {