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.

Raspunde prin e-mail lui