patch 9.1.1267: Vim9: no support for type list/dict<object<any>>
Commit: https://github.com/vim/vim/commit/de8f8f732ac1bcf69899df6ffd27dca9a4e66f3c Author: Yegappan Lakshmanan <yegap...@yahoo.com> Date: Tue Apr 1 20:43:36 2025 +0200 patch 9.1.1267: Vim9: no support for type list/dict<object<any>> Problem: Vim9: no support for type list/dict<object<any>> Solution: add proper support for t_object_any (Yegappan Lakshmanan) closes: #17025 Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/evalfunc.c b/src/evalfunc.c index 125ba5513..f2c69999a 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -308,7 +308,7 @@ arg_object(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) if (type->tt_type == VAR_OBJECT || type_any_or_unknown(type)) return OK; - arg_type_mismatch(&t_object, type, context->arg_idx + 1); + arg_type_mismatch(&t_object_any, type, context->arg_idx + 1); return FAIL; } diff --git a/src/globals.h b/src/globals.h index 7e65af30d..b902e0318 100644 --- a/src/globals.h +++ b/src/globals.h @@ -540,8 +540,8 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE); #define t_super (static_types[84]) #define t_const_super (static_types[85]) -#define t_object (static_types[86]) -#define t_const_object (static_types[87]) +#define t_object_any (static_types[86]) +#define t_const_object_any (static_types[87]) #define t_class (static_types[88]) #define t_const_class (static_types[89]) @@ -731,7 +731,7 @@ EXTERN type_T static_types[96] {VAR_CLASS, 0, 0, TTFLAG_STATIC, &t_bool, NULL, NULL}, {VAR_CLASS, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL, NULL}, - // 86: t_object + // 86: t_object_any {VAR_OBJECT, 0, 0, TTFLAG_STATIC, NULL, NULL, NULL}, {VAR_OBJECT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL, NULL}, diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 22f8bab36..7d65cb61e 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -2430,7 +2430,7 @@ def Test_instanceof() enddef Bar() END - v9.CheckSourceScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected object<Unknown> but got string') + v9.CheckSourceScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected object<any> but got string') lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 30a03cf31..c6d93b2b7 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -564,7 +564,7 @@ def Test_using_null_class() lines =<< trim END vim9script assert_equal(12, type(null_class)) - assert_equal('class<Unknown>', typename(null_class)) + assert_equal('class<any>', typename(null_class)) END v9.CheckSourceSuccess(lines) enddef @@ -643,7 +643,6 @@ def Test_object_not_set() END v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1) - # TODO: this should not give an error but be handled at runtime lines =<< trim END vim9script @@ -660,7 +659,7 @@ def Test_object_not_set() enddef Func() END - v9.CheckSourceFailure(lines, 'E1363: Incomplete type', 1) + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1) # Reference a object variable through a null class object which is stored in a # variable of type "any". @@ -710,7 +709,7 @@ def Test_null_object_assign_compare() def F(): any return nullo enddef - assert_equal('object<Unknown>', typename(F())) + assert_equal('object<any>', typename(F())) var o0 = F() assert_true(o0 == null_object) @@ -12486,37 +12485,325 @@ def Test_method_call_from_list_of_objects() var lines =<< trim END vim9script - class C + class A + var n: list<number> = [100, 101] + def F(): string + return 'A.F' + enddef + endclass + + class B + var name: string + var n: list<number> = [200, 201] + def new(this.name) + enddef def F(): string - return 'C.F' + return 'B.F' enddef endclass - class D - var x: string - def new(this.x) + var obj1 = A.new() + var obj2 = B.new('b1') + + def CheckObjectList() + var objlist = [obj1, obj2] + assert_equal('list<object<any>>', typename(objlist)) + + # Use a member function + assert_equal('A.F', objlist[0].F()) + assert_equal('B.F', objlist[1].F()) + + # Use a member variable on the RHS + assert_equal([100, 101], objlist[0].n) + assert_equal([200, 201], objlist[1].n) + + # Use a member variable on the LHS + objlist[0].n[1] = 110 + objlist[1].n[1] = 210 + assert_equal([100, 110], objlist[0].n) + assert_equal([200, 210], objlist[1].n) + + # Iterate using a for loop + var s1 = [] + for o in objlist + add(s1, o.F()) + endfor + assert_equal(['A.F', 'B.F'], s1) + + # Iterate using foreach() + var s2 = [] + foreach(objlist, (k, v) => add(s2, v.F())) + assert_equal(['A.F', 'B.F'], s2) + + # Add a new list item + objlist->add(B.new('b2')) + assert_equal('b2', objlist[2].name) + enddef + + CheckObjectList() + + var objlist = [A.new(), B.new('b2')] + assert_equal('list<object<any>>', typename(objlist)) + + # Use a member function + assert_equal('A.F', objlist[0].F()) + assert_equal('B.F', objlist[1].F()) + + # Use a member variable on the RHS + assert_equal([100, 101], objlist[0].n) + assert_equal([200, 201], objlist[1].n) + + # Use a member variable on the LHS + objlist[0].n[1] = 110 + objlist[1].n[1] = 210 + assert_equal([100, 110], objlist[0].n) + assert_equal([200, 210], objlist[1].n) + + # Iterate using a for loop + var s1 = [] + for o in objlist + add(s1, o.F()) + endfor + assert_equal(['A.F', 'B.F'], s1) + + # Iterate using foreach() + var s2 = [] + foreach(objlist, (k, v) => add(s2, v.F())) + assert_equal(['A.F', 'B.F'], s2) + + # Add a new list item + objlist->add(B.new('b2')) + assert_equal('b2', objlist[2].name) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + endclass + + class B + endclass + + var objlist = [A.new(), B.new()] + def Fn() + objlist->add(10) enddef - def F(): string - return 'D.F' + + try + Fn() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number') + endtry + + try + objlist->add(10) + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number') + endtry + END + v9.CheckSourceSuccess(lines) + + # Adding an enum to a List of objects should fail + lines =<< trim END + vim9script + class A + endclass + class B + endclass + enum C + Red, + Green, + endenum + var items = [A.new(), B.new()] + def Fn() + items->add(C.Red) enddef - endclass - var obj1 = C.new() - var obj2 = D.new('a') + try + Fn() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>') + endtry - def CheckObjectList() - var items = [obj1, obj2] - assert_equal('list<any>', typename(items)) - assert_equal('C.F', items[0].F()) - assert_equal('D.F', items[1].F()) - enddef + try + items->add(C.Green) + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>') + endtry - CheckObjectList() + var items2 = [C.Red, C.Green] + def Fn2() + items2->add(A.new()) + enddef + try + Fn2() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<A>') + endtry - var items2 = [obj1, obj2] - assert_equal('list<any>', typename(items2)) - assert_equal('C.F', items2[0].F()) - assert_equal('D.F', items2[1].F()) + try + items2->add(B.new()) + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<B>') + endtry + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a dict of objects +def Test_dict_of_objects() + var lines =<< trim END + vim9script + + class A + var n: list<number> = [100, 101] + def F(): string + return 'A.F' + enddef + endclass + + class B + var name: string + var n: list<number> = [200, 201] + def new(this.name) + enddef + def F(): string + return 'B.F' + enddef + endclass + + var obj1 = A.new() + var obj2 = B.new('b1') + + def CheckObjectDict() + var objdict = {o_a: obj1, o_b: obj2} + assert_equal('dict<object<any>>', typename(objdict)) + + # Use a member function + assert_equal('A.F', objdict.o_a.F()) + assert_equal('B.F', objdict.o_b.F()) + + # Use a member variable on the RHS + assert_equal([100, 101], objdict.o_a.n) + assert_equal([200, 201], objdict.o_b.n) + + # Use a member variable on the LHS + objdict.o_a.n[1] = 110 + objdict.o_b.n[1] = 210 + assert_equal([100, 110], objdict.o_a.n) + assert_equal([200, 210], objdict.o_b.n) + + # Iterate using a for loop + var l = [] + for v in values(objdict) + add(l, v.F()) + endfor + assert_equal(['A.F', 'B.F'], l) + + # Iterate using foreach() + l = [] + foreach(objdict, (k, v) => add(l, v.F())) + assert_equal(['A.F', 'B.F'], l) + + # Add a new dict item + objdict['o_b2'] = B.new('b2') + assert_equal('b2', objdict.o_b2.name) + enddef + + CheckObjectDict() + + var objdict = {o_a: A.new(), o_b: B.new('b2')} + assert_equal('dict<object<any>>', typename(objdict)) + assert_equal('A.F', objdict.o_a.F()) + assert_equal('B.F', objdict.o_b.F()) + assert_equal([100, 101], objdict.o_a.n) + assert_equal([200, 201], objdict.o_b.n) + + var l = [] + for v in values(objdict) + add(l, v.F()) + endfor + assert_equal(['A.F', 'B.F'], l) + + # Add a new dict item + objdict['o_b2'] = B.new('b2') + assert_equal('b2', objdict.o_b2.name) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + endclass + class B + endclass + class C + endclass + var objdict = {a: A.new(), b: B.new()} + def Fn() + objdict['c'] = C.new() + enddef + + try + Fn() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number') + endtry + + try + objdict['c'] = C.new() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number') + endtry + END + v9.CheckSourceSuccess(lines) + + # Adding an enum to a Dict of objects should fail + lines =<< trim END + vim9script + class A + endclass + class B + endclass + enum C + Red, + Green, + endenum + var items = {o_a: A.new(), o_b: B.new()} + def Fn() + items['o_c'] = C.Red + enddef + + try + Fn() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>') + endtry + + try + items['o_c'] = C.Green + catch + assert_exception('Vim(var):E1012: Type mismatch; expected object<any> but got enum<C>') + endtry + + var items2 = {red: C.Red, green: C.Green} + def Fn2() + items2['o_a'] = A.new() + enddef + try + Fn2() + catch + assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<A>') + endtry + + try + items2['o_a'] = B.new() + catch + assert_exception('Vim(var):E1012: Type mismatch; expected enum<C> but got object<B>') + endtry END v9.CheckSourceSuccess(lines) enddef diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index d9013ddf5..8456037c7 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5180,7 +5180,7 @@ def Test_null_values() [null_dict, 1, '{}', 4, 'dict<any>'], [null_function, 1, "function('')", 2, 'func(...): unknown'], [null_list, 1, '[]', 3, 'list<any>'], - [null_object, 1, 'object of [unknown]', 13, 'object<Unknown>'], + [null_object, 1, 'object of [unknown]', 13, 'object<any>'], [null_partial, 1, "function('')", 2, 'func(...): unknown'], [null_string, 1, "''", 1, 'string'] ] diff --git a/src/version.c b/src/version.c index c90c7b56e..47104eff4 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 */ +/**/ + 1267, /**/ 1266, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 34a78daef..0a4c3facb 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2498,7 +2498,8 @@ compile_load_lhs( : get_type_on_stack(cctx, 0); if (lhs->lhs_type->tt_type == VAR_CLASS - || lhs->lhs_type->tt_type == VAR_OBJECT) + || (lhs->lhs_type->tt_type == VAR_OBJECT + && lhs->lhs_type != &t_object_any)) { // Check whether the class or object variable is modifiable if (!lhs_class_member_modifiable(lhs, var_start, cctx)) @@ -2522,7 +2523,7 @@ compile_load_lhs( return OK; } - return generate_loadvar(cctx, lhs); + return generate_loadvar(cctx, lhs); } /* diff --git a/src/vim9expr.c b/src/vim9expr.c index 68de73601..0ceff0b85 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -2684,7 +2684,8 @@ compile_subscript( type = get_type_on_stack(cctx, 0); if (type != &t_unknown && (type->tt_type == VAR_CLASS - || type->tt_type == VAR_OBJECT)) + || (type->tt_type == VAR_OBJECT + && type != &t_object_any))) { // class member: SomeClass.varname // class method: SomeClass.SomeMethod() diff --git a/src/vim9instr.c b/src/vim9instr.c index 1b322f64a..d4593ea13 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -756,7 +756,7 @@ generate_SETTYPE( generate_PUSHOBJ(cctx_T *cctx) { RETURN_OK_IF_SKIP(cctx); - if (generate_instr_type(cctx, ISN_PUSHOBJ, &t_object) == NULL) + if (generate_instr_type(cctx, ISN_PUSHOBJ, &t_object_any) == NULL) return FAIL; return OK; } @@ -2142,7 +2142,8 @@ generate_PCALL( RETURN_OK_IF_SKIP(cctx); - if (type->tt_type == VAR_ANY || type->tt_type == VAR_UNKNOWN) + if (type->tt_type == VAR_ANY || type->tt_type == VAR_UNKNOWN + || type == &t_object_any) ret_type = &t_any; else if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL) { @@ -2213,7 +2214,9 @@ generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len) // check for dict type type = get_type_on_stack(cctx, 0); if (type->tt_type != VAR_DICT - && type->tt_type != VAR_ANY && type->tt_type != VAR_UNKNOWN) + && type->tt_type != VAR_OBJECT + && type->tt_type != VAR_ANY + && type->tt_type != VAR_UNKNOWN) { char *tofree; diff --git a/src/vim9type.c b/src/vim9type.c index 03a391a17..2f8509587 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -764,7 +764,7 @@ oc_typval2type(typval_T *tv) if (tv->vval.v_object != NULL) return &tv->vval.v_object->obj_class->class_object_type; - return &t_object; + return &t_object_any; } /* @@ -1307,8 +1307,11 @@ check_type_maybe( return MAYBE; // use runtime type check if (actual->tt_type != VAR_OBJECT) return FAIL; // don't use tt_class - if (actual->tt_class == NULL) - return OK; // A null object matches + if (actual->tt_class == NULL) // null object + return OK; + // t_object_any matches any object except for an enum item + if (expected == &t_object_any && !IS_ENUM(actual->tt_class)) + return OK; // For object method arguments, do a invariant type check in // an extended class. For all others, do a covariance type check. @@ -2122,6 +2125,11 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap) common_type_var_func(type1, type2, dest, type_gap); return; } + else if (type1->tt_type == VAR_OBJECT) + { + *dest = &t_object_any; + return; + } } *dest = &t_any; @@ -2428,7 +2436,7 @@ type_name_class_or_obj(char *name, type_T *type, char **tofree) name = "enum"; } else - class_name = (char_u *)"Unknown"; + class_name = (char_u *)"any"; size_t len = STRLEN(name) + STRLEN(class_name) + 3; *tofree = alloc(len); -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1tzgqB-00Bxux-RR%40256bit.org.