patch 9.0.1780: Vim9 type not defined during object creation

Commit: 
https://github.com/vim/vim/commit/618e47d1cd93954bad26d47e5353b4f1432daa5e
Author: Yegappan Lakshmanan <[email protected]>
Date:   Tue Aug 22 21:29:28 2023 +0200

    patch 9.0.1780: Vim9 type not defined during object creation
    
    Problem:  Vim9 type not defined during object creation
    Solution: Define type during object creation and not during class
              definition, parse mulit-line member initializers, fix lock
              initialization
    
    If type is not specified for a member, set it during object creation
    instead of during class definition.  Add a runtime type check for the
    object member initialization expression
    
    Also, while at it, when copying an object or class, make sure the lock
    is correctly initialized.
    
    And finally, parse multi-line member initializers correctly.
    
    closes: #11957
    closes: #12868
    closes: #12869
    closes: #12881
    
    Signed-off-by: Christian Brabandt <[email protected]>
    Co-authored-by: Yegappan Lakshmanan <[email protected]>
    Co-authored-by: LemonBoy <[email protected]>

diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index a68b97c87..c68288a0c 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -423,6 +423,12 @@ Each member and function name can be used only once.  It 
is not possible to
 define a function with the same name and different type of arguments.
 
 
+Member Initialization ~
+If the type of a member is not explicitly specified in a class, then it is set
+to "any" during class definition.  When an object is instantiated from the
+class, then the type of the member is set.
+
+
 Extending a class ~
                                                        *extends*
 A class can extend one other class. *E1352* *E1353* *E1354*
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index dbd4c3bcb..7e33c6d44 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -210,6 +210,17 @@ def Test_class_basic()
     var v = a.Foo(,)
   END
   v9.CheckScriptFailure(lines, 'E15:')
+
+  lines =<< trim END
+  vim9script
+  class A
+    this.y = {
+      X: 1
+    }
+  endclass
+  var a = A.new()
+  END
+  v9.CheckScriptSuccess(lines)
 enddef
 
 def Test_class_defined_twice()
@@ -668,14 +679,28 @@ def Test_class_object_member_inits()
   END
   v9.CheckScriptFailure(lines, 'E1022:')
 
+  # If the type is not specified for a member, then it should be set during
+  # object creation and not when defining the class.
   lines =<< trim END
       vim9script
-      class TextPosition
-        this.lnum = v:none
+
+      var init_count = 0
+      def Init(): string
+        init_count += 1
+        return 'foo'
+      enddef
+
+      class A
+        this.str1 = Init()
+        this.str2: string = Init()
         this.col = 1
       endclass
+
+      assert_equal(init_count, 0)
+      var a = A.new()
+      assert_equal(init_count, 2)
   END
-  v9.CheckScriptFailure(lines, 'E1330:')
+  v9.CheckScriptSuccess(lines)
 
   # Test for initializing an object member with an unknown variable/type
   lines =<< trim END
@@ -683,8 +708,9 @@ def Test_class_object_member_inits()
     class A
        this.value = init_val
     endclass
+    var a = A.new()
   END
-  v9.CheckScriptFailureList(lines, ['E121:', 'E1329:'])
+  v9.CheckScriptFailure(lines, 'E1001:')
 enddef
 
 def Test_class_object_member_access()
@@ -2625,4 +2651,67 @@ def Test_new_return_type()
   v9.CheckScriptFailure(lines, 'E1365:')
 enddef
 
+" Test for checking a member initialization type at run time.
+def Test_runtime_type_check_for_member_init()
+  var lines =<< trim END
+    vim9script
+
+    var retnum: bool = false
+
+    def F(): any
+        retnum = !retnum
+        if retnum
+            return 1
+        else
+            return "hello"
+        endif
+    enddef
+
+    class C
+        this._foo: bool = F()
+    endclass
+
+    var c1 = C.new()
+    var c2 = C.new()
+  END
+  v9.CheckScriptFailure(lines, 'E1012:')
+enddef
+
+" Test for locking a variable referring to an object and reassigning to another
+" object.
+def Test_object_lockvar()
+  var lines =<< trim END
+    vim9script
+
+    class C
+      this.val: number
+      def new(this.val)
+      enddef
+    endclass
+
+    var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
+    lockvar 2 some_dict
+
+    var current: C
+    current = some_dict['c']
+    assert_equal(3, current.val)
+    current = some_dict['b']
+    assert_equal(2, current.val)
+
+    def F()
+      current = some_dict['c']
+    enddef
+
+    def G()
+      current = some_dict['b']
+    enddef
+
+    F()
+    assert_equal(3, current.val)
+    G()
+    assert_equal(2, current.val)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 3ca3167cb..7b0615208 100644
--- a/src/version.c
+++ b/src/version.c
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1780,
 /**/
     1779,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index e4130459b..00b1f7d98 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -67,66 +67,48 @@ parse_member(
            return FAIL;
     }
 
-    char_u *expr_start = skipwhite(type_arg);
-    char_u *expr_end = expr_start;
-    if (type == NULL && *expr_start != '=')
+    char_u *init_arg = skipwhite(type_arg);
+    if (type == NULL && *init_arg != '=')
     {
        emsg(_(e_type_or_initialization_required));
        return FAIL;
     }
 
-    if (*expr_start == '=')
+    if (init_expr == NULL && *init_arg == '=')
     {
-       if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
+       emsg(_(e_cannot_initialize_member_in_interface));
+       return FAIL;
+    }
+
+    if (*init_arg == '=')
+    {
+       evalarg_T evalarg;
+       char_u *expr_start, *expr_end;
+
+       if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
        {
            semsg(_(e_white_space_required_before_and_after_str_at_str),
                                                        "=", type_arg);
            return FAIL;
        }
-       expr_start = skipwhite(expr_start + 1);
+       init_arg = skipwhite(init_arg + 1);
 
-       expr_end = expr_start;
-       evalarg_T evalarg;
        fill_evalarg_from_eap(&evalarg, eap, FALSE);
-       skip_expr(&expr_end, NULL);
+       (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, 
&evalarg);
 
+       // No type specified for the member.  Set it to "any" and the correct 
type will be
+       // set when the object is instantiated.
        if (type == NULL)
-       {
-           // No type specified, use the type of the initializer.
-           typval_T tv;
-           tv.v_type = VAR_UNKNOWN;
-           char_u *expr = expr_start;
-           int res = eval0(expr, &tv, eap, &evalarg);
+           type = &t_any;
 
-           if (res == OK)
-           {
-               type = typval2type(&tv, get_copyID(), type_list,
-                                                      TVTT_DO_MEMBER);
-               clear_tv(&tv);
-           }
-           if (type == NULL)
-           {
-               semsg(_(e_cannot_get_object_member_type_from_initializer_str),
-                       expr_start);
-               clear_evalarg(&evalarg, NULL);
-               return FAIL;
-           }
-       }
+       *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+       // Free the memory pointed by expr_start.
        clear_evalarg(&evalarg, NULL);
     }
-    if (!valid_declaration_type(type))
+    else if (!valid_declaration_type(type))
        return FAIL;
 
     *type_ret = type;
-    if (expr_end > expr_start)
-    {
-       if (init_expr == NULL)
-       {
-           emsg(_(e_cannot_initialize_member_in_interface));
-           return FAIL;
-       }
-       *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
-    }
     return OK;
 }
 
@@ -1740,9 +1722,13 @@ inside_class(cctx_T *cctx_arg, class_T *cl)
     void
 copy_object(typval_T *from, typval_T *to)
 {
-    *to = *from;
-    if (to->vval.v_object != NULL)
+    if (from->vval.v_object == NULL)
+       to->vval.v_object = NULL;
+    else
+    {
+       to->vval.v_object = from->vval.v_object;
        ++to->vval.v_object->obj_refcount;
+    }
 }
 
 /*
@@ -1787,9 +1773,13 @@ object_unref(object_T *obj)
     void
 copy_class(typval_T *from, typval_T *to)
 {
-    *to = *from;
-    if (to->vval.v_class != NULL)
+    if (from->vval.v_class == NULL)
+       to->vval.v_class = NULL;
+    else
+    {
+       to->vval.v_class = from->vval.v_class;
        ++to->vval.v_class->class_refcount;
+    }
 }
 
 /*
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 028b0ca15..09b4bf143 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3150,6 +3150,19 @@ compile_def_function(
                        semsg(_(e_trailing_characters_str), expr);
                        goto erret;
                    }
+
+                   type_T      *type = get_type_on_stack(&cctx, 0);
+                   if (m->ocm_type->tt_type != type->tt_type)
+                   {
+                       // The type of the member initialization expression is
+                       // determined at run time.  Add a runtime type check.
+                       where_T where = WHERE_INIT;
+                       where.wt_kind = WT_MEMBER;
+                       where.wt_func_name = (char *)m->ocm_name;
+                       if (need_type_where(type, m->ocm_type, FALSE, -1,
+                                   where, &cctx, FALSE, FALSE) == FAIL)
+                           goto erret;
+                   }
                }
                else
                    push_default_value(&cctx, m->ocm_type->tt_type,

-- 
-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1qYXJH-00Dr2q-0v%40256bit.org.

Raspunde prin e-mail lui