patch 9.1.0160: Vim9: Add support for using a class type of itself in an object 
method

Commit: 
https://github.com/vim/vim/commit/35b867b685cedbcbba9d44695077ecc9a6995f4c
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Sat Mar 9 15:44:19 2024 +0100

    patch 9.1.0160: Vim9: Add support for using a class type of itself in an 
object method
    
    Problem:  Add support for using a class type of itself in an object
              method (thinca)
    Solution: Vim9: Add support for using a class type of itself in an
              object method (Yegappan Lakshmanan)
    
    fixes: #12369
    closes: #14165
    
    Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/evalvars.c b/src/evalvars.c
index 856e47511..de9d5b26b 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -3830,7 +3830,7 @@ set_var(
  * If the variable already exists and "is_const" is FALSE the value is updated.
  * Otherwise the variable is created.
  */
-    void
+    int
 set_var_const(
     char_u     *name,
     scid_T     sid,
@@ -3854,6 +3854,7 @@ set_var_const(
     int                var_in_autoload = FALSE;
     int                flags = flags_arg;
     int                free_tv_arg = !copy;  // free tv_arg if not used
+    int                rc = FAIL;
 
     if (sid != 0)
     {
@@ -4127,10 +4128,14 @@ set_var_const(
        // values.
        item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
 
+    rc = OK;
+
 failed:
     vim_free(name_tofree);
     if (free_tv_arg)
        clear_tv(tv_arg);
+
+    return rc;
 }
 
 /*
diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro
index 9879a401f..ea14fe5ce 100644
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -77,7 +77,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val);
 void delete_var(hashtab_T *ht, hashitem_T *hi);
 int before_set_vvar(char_u *varname, dictitem_T *di, typval_T *tv, int copy, 
int *type_error);
 void set_var(char_u *name, typval_T *tv, int copy);
-void set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T 
*tv_arg, int copy, int flags_arg, int var_idx);
+int set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T 
*tv_arg, int copy, int flags_arg, int var_idx);
 int var_check_permission(dictitem_T *di, char_u *name);
 int var_check_ro(int flags, char_u *name, int use_gettext);
 int var_check_lock(int flags, char_u *name, int use_gettext);
diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim
index 47aeab3b1..dd2f012c0 100644
--- a/src/testdir/shared.vim
+++ b/src/testdir/shared.vim
@@ -301,7 +301,7 @@ func GetVimCommand(...)
   let cmd .= ' --not-a-term'
   let cmd .= ' --gui-dialog-file guidialogfile'
   " remove any environment variables
-  let cmd = substitute(cmd, '[A-Z_]*=\S\+ *', '', 'g')
+  let cmd = substitute(cmd, '[A-Z_]\+=\S\+ *', '', 'g')
 
   " If using valgrind, make sure every run uses a different log file.
   if cmd =~ 'valgrind.*--log-file='
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 12e3c48a3..17252605f 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -67,6 +67,22 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
 
+  # Try to define a class with the same name as an existing variable
+  lines =<< trim END
+    vim9script
+    var Something: list<number> = [1]
+    class Thing
+    endclass
+    interface Api
+    endinterface
+    class Something extends Thing implements Api
+      var v1: string = ''
+      def Foo()
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 7)
+
   # Use old "this." prefixed member variable declaration syntax (without 
initialization)
   lines =<< trim END
     vim9script
@@ -10272,4 +10288,65 @@ func Test_object_string()
   call v9.CheckSourceSuccess(lines)
 endfunc
 
+" Test for using a class in the class definition
+def Test_Ref_Class_Within_Same_Class()
+  var lines =<< trim END
+    vim9script
+    class A
+      var n: number = 0
+      def Equals(other: A): bool
+        return this.n == other.n
+      enddef
+    endclass
+
+    var a1 = A.new(10)
+    var a2 = A.new(10)
+    var a3 = A.new(20)
+    assert_equal(true, a1.Equals(a2))
+    assert_equal(false, a2.Equals(a3))
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class Foo
+      var num: number
+      def Clone(): Foo
+        return Foo.new(this.num)
+      enddef
+    endclass
+
+    var f1 = Foo.new(1)
+
+    def F()
+      var f2: Foo = f1.Clone()
+      assert_equal(false, f2 is f1)
+      assert_equal(true, f2.num == f1.num)
+    enddef
+    F()
+
+    var f3: Foo = f1.Clone()
+    assert_equal(false, f3 is f1)
+    assert_equal(true, f3.num == f1.num)
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # Test for trying to use a class to extend when defining the same class
+  lines =<< trim END
+    vim9script
+    class A extends A
+    endclass
+  END
+  v9.CheckScriptFailure(lines, 'E1354: Cannot extend A', 3)
+
+  # Test for trying to use a class to implement when defining the same class
+  lines =<< trim END
+    vim9script
+    class A implements A
+    endclass
+  END
+  v9.CheckScriptFailure(lines, 'E1347: Not a valid interface: A', 3)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 3d6c36d3e..507c4591a 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 */
+/**/
+    160,
 /**/
     159,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index 735752019..7227c3dc2 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -208,7 +208,7 @@ add_member(
  * "parent_count" is the number of members in the parent class
  * "members" will be set to the newly allocated array of members and
  * "member_count" set to the number of members.
- * Returns OK or FAIL.
+ * Returns OK on success and FAIL on memory allocation failure.
  */
     static int
 add_members_to_class(
@@ -301,6 +301,7 @@ object_index_from_itf_index(class_T *itf, int is_method, 
int idx, class_T *cl)
  */
     static int
 validate_extends_class(
+    class_T *cl,
     char_u  *extends_name,
     class_T **extends_clp,
     int            is_class)
@@ -308,6 +309,12 @@ validate_extends_class(
     typval_T   tv;
     int                success = FALSE;
 
+    if (STRCMP(cl->class_name, extends_name) == 0)
+    {
+       semsg(_(e_cannot_extend_str), extends_name);
+       return success;
+    }
+
     tv.v_type = VAR_UNKNOWN;
     if (eval_variable_import(extends_name, &tv) == FAIL)
     {
@@ -1642,6 +1649,36 @@ early_ret:
     garray_T objmethods;
     ga_init2(&objmethods, sizeof(ufunc_T *), 10);
 
+    class_T *cl = NULL;
+    class_T *extends_cl = NULL;  // class from "extends" argument
+    class_T **intf_classes = NULL;
+
+    cl = ALLOC_CLEAR_ONE(class_T);
+    if (cl == NULL)
+       goto cleanup;
+    if (!is_class)
+       cl->class_flags = CLASS_INTERFACE;
+    else if (is_abstract)
+       cl->class_flags = CLASS_ABSTRACT;
+
+    cl->class_refcount = 1;
+    cl->class_name = vim_strnsave(name_start, name_end - name_start);
+    if (cl->class_name == NULL)
+       goto cleanup;
+
+    // Add the class to the script-local variables.
+    // TODO: handle other context, e.g. in a function
+    // TODO: does uf_hash need to be cleared?
+    typval_T tv;
+    tv.v_type = VAR_CLASS;
+    tv.vval.v_class = cl;
+    is_export = class_export;
+    SOURCING_LNUM = start_lnum;
+    int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
+                                               NULL, &tv, FALSE, 0, 0);
+    if (rc == FAIL)
+       goto cleanup;
+
     /*
      * Go over the body of the class/interface until "endclass" or
      * "endinterface" is found.
@@ -1981,15 +2018,13 @@ early_ret:
     }
     vim_free(theline);
 
-    class_T *extends_cl = NULL;  // class from "extends" argument
-
     /*
      * Check a few things before defining the class.
      */
 
     // Check the "extends" class is valid.
     if (success && extends != NULL)
-       success = validate_extends_class(extends, &extends_cl, is_class);
+       success = validate_extends_class(cl, extends, &extends_cl, is_class);
     VIM_CLEAR(extends);
 
     // Check the new object methods to make sure their access (public or
@@ -2016,8 +2051,6 @@ early_ret:
        success = validate_abstract_class_methods(&classfunctions,
                                                &objmethods, extends_cl);
 
-    class_T **intf_classes = NULL;
-
     // Check all "implements" entries are valid.
     if (success && ga_impl.ga_len > 0)
     {
@@ -2032,24 +2065,10 @@ early_ret:
        success = check_func_arg_names(&classfunctions, &objmethods,
                                                        &classmembers);
 
-    class_T *cl = NULL;
     if (success)
     {
        // "endclass" encountered without failures: Create the class.
 
-       cl = ALLOC_CLEAR_ONE(class_T);
-       if (cl == NULL)
-           goto cleanup;
-       if (!is_class)
-           cl->class_flags = CLASS_INTERFACE;
-       else if (is_abstract)
-           cl->class_flags = CLASS_ABSTRACT;
-
-       cl->class_refcount = 1;
-       cl->class_name = vim_strnsave(name_start, name_end - name_start);
-       if (cl->class_name == NULL)
-           goto cleanup;
-
        if (extends_cl != NULL)
        {
            cl->class_extends = extends_cl;
@@ -2136,41 +2155,10 @@ early_ret:
        // TODO:
        // - Fill hashtab with object members and methods ?
 
-       // Add the class to the script-local variables.
-       // TODO: handle other context, e.g. in a function
-       // TODO: does uf_hash need to be cleared?
-       typval_T tv;
-       tv.v_type = VAR_CLASS;
-       tv.vval.v_class = cl;
-       is_export = class_export;
-       SOURCING_LNUM = start_lnum;
-       set_var_const(cl->class_name, current_sctx.sc_sid,
-                                                      NULL, &tv, FALSE, 0, 0);
        return;
     }
 
 cleanup:
-    if (cl != NULL)
-    {
-       vim_free(cl->class_name);
-       vim_free(cl->class_class_functions);
-       if (cl->class_interfaces != NULL)
-       {
-           for (int i = 0; i < cl->class_interface_count; ++i)
-               vim_free(cl->class_interfaces[i]);
-           vim_free(cl->class_interfaces);
-       }
-       if (cl->class_interfaces_cl != NULL)
-       {
-           for (int i = 0; i < cl->class_interface_count; ++i)
-               class_unref(cl->class_interfaces_cl[i]);
-           vim_free(cl->class_interfaces_cl);
-       }
-       vim_free(cl->class_obj_members);
-       vim_free(cl->class_obj_methods);
-       vim_free(cl);
-    }
-
     vim_free(extends);
     class_unref(extends_cl);
 

-- 
-- 
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 on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1riyB9-0098OQ-0w%40256bit.org.

Raspunde prin e-mail lui