patch 9.0.1909: Vim9: problem calling class method from other class

Commit: 
https://github.com/vim/vim/commit/00cd18222ee1551c65228e9556c158624507fc7a
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Mon Sep 18 19:56:49 2023 +0200

    patch 9.0.1909: Vim9: problem calling class method from other class
    
    Problem:  Vim9: problem calling class method from other class
    Solution: Fix this problem, fix readonly object access, update error
              messages.
    
    Calling a class method from another method without the class name prefix
    doesn't work properly.
    
    A readonly object variable is modifiable outside the class using a
    nested object assignment.
    
    Remove the unused E1338 error message.
    
    Update error messages.
    
    closes: #13116
    
    Signed-off-by: Christian Brabandt <c...@256bit.org>
    Co-authored-by: Yegappan Lakshmanan <yegap...@yahoo.com>

diff --git a/runtime/doc/tags b/runtime/doc/tags
index 907d290f3..b754f0830 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4418,13 +4418,11 @@ E1325   vim9class.txt   /*E1325*
 E1326  vim9class.txt   /*E1326*
 E1327  vim9class.txt   /*E1327*
 E1328  vim9class.txt   /*E1328*
-E1329  vim9class.txt   /*E1329*
 E133   userfunc.txt    /*E133*
 E1330  vim9class.txt   /*E1330*
 E1331  vim9class.txt   /*E1331*
 E1332  vim9class.txt   /*E1332*
 E1333  vim9class.txt   /*E1333*
-E1334  vim9class.txt   /*E1334*
 E1335  vim9class.txt   /*E1335*
 E1336  options.txt     /*E1336*
 E1337  vim9class.txt   /*E1337*
@@ -4458,9 +4456,27 @@ E1361    syntax.txt      /*E1361*
 E1362  vim9class.txt   /*E1362*
 E1363  vim9class.txt   /*E1363*
 E1364  recover.txt     /*E1364*
+E1365  vim9class.txt   /*E1365*
+E1366  vim9class.txt   /*E1366*
+E1367  vim9class.txt   /*E1367*
+E1368  vim9class.txt   /*E1368*
+E1369  vim9class.txt   /*E1369*
 E137   starting.txt    /*E137*
 E1370  vim9class.txt   /*E1370*
+E1371  vim9class.txt   /*E1371*
+E1372  vim9class.txt   /*E1372*
+E1373  vim9class.txt   /*E1373*
+E1374  vim9class.txt   /*E1374*
+E1375  vim9class.txt   /*E1375*
+E1376  vim9class.txt   /*E1376*
+E1377  vim9class.txt   /*E1377*
+E1378  vim9class.txt   /*E1378*
+E1379  vim9class.txt   /*E1379*
 E138   starting.txt    /*E138*
+E1380  vim9class.txt   /*E1380*
+E1381  vim9class.txt   /*E1381*
+E1382  vim9class.txt   /*E1382*
+E1383  vim9class.txt   /*E1383*
 E139   message.txt     /*E139*
 E140   message.txt     /*E140*
 E1400  builtin.txt     /*E1400*
@@ -6367,7 +6383,7 @@ cino-{    indent.txt      /*cino-{*
 cino-} indent.txt      /*cino-}*
 cinoptions-values      indent.txt      /*cinoptions-values*
 class  vim9class.txt   /*class*
-class-function vim9class.txt   /*class-function*
+class-method   vim9class.txt   /*class-method*
 clear-undo     undo.txt        /*clear-undo*
 clearmatches() builtin.txt     /*clearmatches()*
 client-server  remote.txt      /*client-server*
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index 20ad4bbab..c106bb60e 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -1,4 +1,4 @@
-*vim9class.txt*        For Vim version 9.0.  Last change: 2023 Mar 22
+*vim9class.txt*        For Vim version 9.0.  Last change: 2023 Sep 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -11,7 +11,7 @@ Vim9 classes, objects, interfaces, types and enums.
 
 1.  Overview                   |Vim9-class-overview|
 2.  A simple class             |Vim9-simple-class|
-3.  Class members and functions        |Vim9-class-member|
+3.  Class variables and methods        |Vim9-class-member|
 4.  Using an abstract class    |Vim9-abstract-class|
 5.  Using an interface         |Vim9-using-interface|
 6.  More class details         |Vim9-class|
@@ -139,11 +139,13 @@ changed at any time, you can make it public: >
 
 Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
 "pos.lnum" directly above will no longer give an error.
-                                                       *E1334*
+                                                       *E1326*
 If you try to set an object member that doesn't exist you get an error: >
        pos.other = 9
-<      E1334: Object member not found: other ~
+<      E1326: Member not found on object "TextPosition": other ~
 
+                                                       *E1376*
+A object member cannot be accessed using the class name.
 
 Private members ~
                                                        *E1332* *E1333*
@@ -176,9 +178,9 @@ number to the total number of lines: >
           endif
           return this._lnum
        enddef
-
-
+<
 Private methods ~
+                                                       *E1366*
 If you want object methods to be accessible only from other methods of the
 same class and not used from outside the class, then you can make them
 private.  This is done by prefixing the method name with an underscore: >
@@ -252,16 +254,17 @@ If the class extends a parent class, the same thing 
happens.  In the second
 step the members of the parent class are done first.  There is no need to call
 "super()" or "new()" on the parent.
 
+                                               *E1365*
 When defining the new() method the return type should not be specified.  It
 always returns an object of the class.
 
 ==============================================================================
 
-3.  class members and functions                        *Vim9-class-member*
+3.  Class Variables and Methods                        *Vim9-class-member*
 
-                                               *:static* *E1337* *E1338*
+                                           *:static* *E1337* *E1338* *E1368*
 Class members are declared with "static".  They are used by the name without a
-prefix: >
+prefix in the class where they are defined: >
 
        class OtherThing
           this.size: number
@@ -275,6 +278,10 @@ prefix: >
 Since the name is used as-is, shadowing the name by a function argument name
 or local variable name is not allowed.
 
+                                                       *E1374* *E1375*
+To access a class member outside of the class where it is defined, the class
+name prefix must be used.  A class member cannot be accessed using an object.
+
 Just like object members the access can be made private by using an underscore
 as the first character in the name, and it can be made public by prefixing
 "public": >
@@ -285,10 +292,11 @@ as the first character in the name, and it can be made 
public by prefixing
        public static result: number  # anybody can read and write
     endclass
 <
-                                                       *class-function*
-Class functions are also declared with "static".  They have no access to
-object members, they cannot use the "this" keyword. >
-
+                                                       *class-method*
+Class methods are also declared with "static".  They can use the class
+variables but they have no access to the object variables, they cannot use the
+"this" keyword.
+>
        class OtherThing
           this.size: number
           static totalSize: number
@@ -301,8 +309,9 @@ object members, they cannot use the "this" keyword. >
           enddef
        endclass
 
-Inside the class the function can be called by name directly, outside the
-class the class name must be prefixed: `OtherThing.ClearTotalSize()`.
+Inside the class the class method can be called by name directly, outside the
+class the class name must be prefixed: `OtherThing.ClearTotalSize()`.  To use
+a super class method in a child class, the class name must be prefixed.
 
 Just like object methods the access can be made private by using an underscore
 as the first character in the method name: >
@@ -312,7 +321,7 @@ as the first character in the method name: >
            echo "Foo"
        enddef
        def Bar()
-           OtherThing._Foo()
+           _Foo()
        enddef
     endclass
 <
@@ -320,6 +329,31 @@ as the first character in the method name: >
 Note that constructors cannot be declared as "static", because they always
 are.
 
+To access the class methods and class variables of a super class in an
+extended class, the class name prefix should be used just as from anywhere
+outside of the defining class: >
+
+    vim9script
+    class Vehicle
+       static nextID: number = 1000
+       static def GetID(): number
+           nextID += 1
+           return nextID
+       enddef
+    endclass
+    class Car extends Vehicle
+       this.myID: number
+       def new()
+           this.myID = Vehicle.GetID()
+       enddef
+    endclass
+<
+Class variables and methods are not inherited by a child class.  A child class
+can declare a static variable or a method with the same name as the one in the
+super class.  Depending on the class where the member is used the
+corresponding class member will be used.  The type of the class member in a
+child class can be different from that in the super class.
+
 ==============================================================================
 
 4.  Using an abstract class                    *Vim9-abstract-class*
@@ -358,16 +392,19 @@ class, for which objects can be created.  Example: >
 An abstract class is defined the same way as a normal class, except that it
 does not have any new() method. *E1359*
 
-                                               *abstract-method*
+                                               *abstract-method* *E1371* 
*E1372*
 An abstract method can be defined in an abstract class by using the "abstract"
 prefix when defining the function: >
 
        abstract class Shape
           abstract def Draw()
+          abstract static def SetColor()
        endclass
-
+<
+                                               *E1373*
 A class extending the abstract class must implement all the abstract methods.
-Class methods in an abstract class can also be abstract methods.
+The signature (arguments, argument types and return type) must be exactly the
+same.  Class methods in an abstract class can also be abstract methods.
 
 ==============================================================================
 
@@ -409,9 +446,10 @@ a number.  This example extends the one above: >
              return this.base * this.height / 2
           enddef
        endclass
-
+<
+                                       *E1348* *E1349* *E1367* *E1382* *E1383*
 If a class declares to implement an interface, all the items specified in the
-interface must appear in the class, with the same types. *E1348* *E1349*
+interface must appear in the class, with the same types.
 
 The interface name can be used as a type: >
 
@@ -422,7 +460,14 @@ The interface name can be used as a type: >
        for shape in shapes
           echo $'the surface is {shape.Surface()}'
        endfor
+<
+                                               *E1378* *E1379* *E1380*
+An interface can have only instance variables (read-only and read-write
+access) and methods.  An interface cannot contain private variables, private
+methods, class variables and class methods.
 
+An interface can extend another interface using "extends".  The sub-interface
+inherits all the instance variables and methods from the super interface.
 
 ==============================================================================
 
@@ -464,9 +509,12 @@ once.  They can appear in any order, although this order 
is recommended: >
        extends ClassName
        implements InterfaceName, OtherInterface
        specifies SomeInterface
-<                                                      *E1355*
+<                                                      *E1355* *E1369*
 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.
+define a function with the same name and different type of arguments.  It is
+not possible to use a public and private member variable with the same name.
+A object variable name used in a super class cannot be reused in a child
+class.
 
 
 Member Initialization ~
@@ -491,6 +539,10 @@ Object methods of the base class can be overruled.  The 
signature (arguments,
 argument types and return type) must be exactly the same.  The method of the
 base class can be called by prefixing "super.".
 
+                                               *E1377*
+The access level of a method (public or private) in a child class should be
+the same as the super class.
+
 Other object methods of the base class are taken over by the child class.
 
 Class functions, including functions starting with "new", can be overruled,
@@ -523,18 +575,26 @@ interface, which is often done in many languages, 
especially Java.
 
 
 Items in a class ~
-                                               *E1318* *E1325* *E1326*
+                                               *E1318* *E1325*
 Inside a class, in between `:class` and `:endclass`, these items can appear:
 - An object member declaration: >
-       this._memberName: memberType
-       this.memberName: memberType
-       public this.memberName: memberType
+       this._privateMemberName: memberType
+       this.readonlyMemberName: memberType
+       public this.readwriteMemberName: memberType
+- A class member declaration: >
+       static this._privateMemberName: memberType
+       static this.readonlyMemberName: memberType
+       static public this.readwriteMemberName: memberType
 - A constructor method: >
        def new(arguments)
        def newName(arguments)
+- A class method: >
+       static def SomeMethod(arguments)
+       static def _PrivateMethod(arguments)
 - An object method: >
        def SomeMethod(arguments)
-<                                                      *E1329*
+       def _PrivateMethod(arguments)
+
 For the object member the type must be specified.  The best way is to do this
 explicitly with ": {type}".  For simple types you can also use an initializer,
 such as "= 123", and Vim will see that the type is a number.  Avoid doing this
@@ -573,6 +633,8 @@ An interface name must start with an uppercase letter. 
*E1343*
 The "Has" prefix can be used to make it easier to guess this is an interface
 name, with a hint about what it provides.
 An interface can only be defined in a |Vim9| script file.  *E1342*
+An interface cannot "implement" another interface but it can "extend" another
+interface. *E1381*
 
 
 null object ~
diff --git a/src/errors.h b/src/errors.h
index 1db70786b..f1373a42c 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3418,8 +3418,7 @@ EXTERN char e_internal_error_shortmess_too_long[]
 #ifdef FEAT_EVAL
 EXTERN char e_class_member_str_not_found_in_class_str[]
        INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\""));
-EXTERN char e_interface_static_direct_access_str[]
-       INIT(= N_("E1338: Cannot directly access interface \"%s\" static member 
\"%s\""));
+// E1338 unused
 #endif
 #ifdef FEAT_PROP_POPUP
 EXTERN char 
e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
@@ -3506,11 +3505,11 @@ EXTERN char 
e_object_member_str_accessible_only_using_object_str[]
        INIT(= N_("E1376: Object member \"%s\" accessible only using class 
\"%s\" object"));
 EXTERN char e_method_str_of_class_str_has_different_access[]
        INIT(= N_("E1377: Access level of method \"%s\" is different in class 
\"%s\""));
-EXTERN char e_static_cannot_be_used_in_interface[]
-       INIT(= N_("E1378: Static cannot be used in an interface"));
-EXTERN char e_private_variable_str_in_interface[]
+EXTERN char e_static_member_not_supported_in_interface[]
+       INIT(= N_("E1378: Static member not supported in an interface"));
+EXTERN char e_private_variable_not_supported_in_interface[]
        INIT(= N_("E1379: Private variable not supported in an interface"));
-EXTERN char e_private_method_str_in_interface[]
+EXTERN char e_private_method_not_supported_in_interface[]
        INIT(= N_("E1380: Private method not supported in an interface"));
 EXTERN char e_interface_cannot_use_implements[]
        INIT(= N_("E1381: Interface cannot use \"implements\""));
diff --git a/src/eval.c b/src/eval.c
index 523546957..0f952ee3e 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1180,14 +1180,6 @@ get_lval(
            return NULL;
        lp->ll_tv = &v->di_tv;
     }
-    if (vim9script && writing && lp->ll_tv->v_type == VAR_CLASS
-           && (lp->ll_tv->vval.v_class->class_flags & CLASS_INTERFACE) != 0)
-    {
-       if (!quiet)
-           semsg(_(e_interface_static_direct_access_str),
-                           lp->ll_tv->vval.v_class->class_name, lp->ll_name);
-       return NULL;
-    }
 
     if (vim9script && (flags & GLV_NO_DECL) == 0)
     {
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index 1448f1915..d09f65457 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -1,7 +1,7 @@
 /* vim9class.c */
 int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T 
*cl, int is_static);
 void ex_class(exarg_T *eap);
-type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u 
*name_end, int *member_idx, ocmember_T **m);
+type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u 
*name_end, int *member_idx);
 void ex_enum(exarg_T *eap);
 void ex_type(exarg_T *eap);
 int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int 
verbose);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 5e3b94559..fa621b25a 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -4,12 +4,14 @@ source check.vim
 import './vim9.vim' as v9
 
 def Test_class_basic()
+  # Class supported only in "vim9script"
   var lines =<< trim END
       class NotWorking
       endclass
   END
   v9.CheckSourceFailure(lines, 'E1316:')
 
+  # First character in a class name should be capitalized.
   lines =<< trim END
       vim9script
       class notWorking
@@ -17,6 +19,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1314:')
 
+  # Only alphanumeric characters are supported in a class name
   lines =<< trim END
       vim9script
       class Not@working
@@ -24,6 +27,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1315:')
 
+  # Unsupported keyword (instead of class)
   lines =<< trim END
       vim9script
       abstract noclass Something
@@ -31,6 +35,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E475:')
 
+  # Only the completed word "class" should be recognized
   lines =<< trim END
       vim9script
       abstract classy Something
@@ -38,6 +43,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E475:')
 
+  # The complete "endclass" should be specified.
   lines =<< trim END
       vim9script
       class Something
@@ -45,6 +51,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1065:')
 
+  # Additional words after "endclass"
   lines =<< trim END
       vim9script
       class Something
@@ -52,6 +59,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E488:')
 
+  # Additional commands after "endclass"
   lines =<< trim END
       vim9script
       class Something
@@ -59,6 +67,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E488:')
 
+  # Use "this" without any member variable name
   lines =<< trim END
       vim9script
       class Something
@@ -67,6 +76,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1317:')
 
+  # Use "this." without any member variable name
   lines =<< trim END
       vim9script
       class Something
@@ -75,6 +85,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1317:')
 
+  # Space between "this" and ".<variable>"
   lines =<< trim END
       vim9script
       class Something
@@ -83,6 +94,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1317:')
 
+  # Space between "this." and the member variable name
   lines =<< trim END
       vim9script
       class Something
@@ -91,6 +103,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1317:')
 
+  # Use "that" instead of "this"
   lines =<< trim END
       vim9script
       class Something
@@ -100,6 +113,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: 
that.count')
 
+  # Member variable without a type or initialization
   lines =<< trim END
       vim9script
       class Something
@@ -108,6 +122,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1022:')
 
+  # Use a non-existing member variable in new()
   lines =<< trim END
       vim9script
       class Something
@@ -117,8 +132,9 @@ def Test_class_basic()
       endclass
       var obj = Something.new()
   END
-  v9.CheckSourceFailure(lines, 'E1089:')
+  v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Something": 
state')
 
+  # Space before ":" in a member variable declaration
   lines =<< trim END
       vim9script
       class Something
@@ -127,6 +143,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1059:')
 
+  # No space after ":" in a member variable declaration
   lines =<< trim END
       vim9script
       class Something
@@ -204,6 +221,8 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E1324: Using an object as a String')
 
+  # Test creating a class with member variables and methods, calling a object
+  # method.  Check for using type() and typename() with a class and an object.
   lines =<< trim END
       vim9script
 
@@ -270,6 +289,7 @@ def Test_class_basic()
   END
   v9.CheckSourceFailure(lines, 'E15:')
 
+  # Use a multi-line initialization for a member variable
   lines =<< trim END
   vim9script
   class A
@@ -347,6 +367,7 @@ def Test_class_interface_wrong_end()
 enddef
 
 def Test_object_not_set()
+  # Use an uninitialized object in script context
   var lines =<< trim END
       vim9script
 
@@ -360,6 +381,7 @@ def Test_object_not_set()
   END
   v9.CheckSourceFailure(lines, 'E1360:')
 
+  # Use an uninitialized object from a def function
   lines =<< trim END
       vim9script
 
@@ -378,6 +400,8 @@ def Test_object_not_set()
   END
   v9.CheckSourceFailure(lines, 'E1360:')
 
+  # Pass an uninitialized object variable to a "new" function and try to call 
an
+  # object method.
   lines =<< trim END
       vim9script
 
@@ -418,6 +442,7 @@ def Test_object_not_set()
   v9.CheckSourceFailure(lines, 'E1363:')
 enddef
 
+" Null object assignment and comparison
 def Test_null_object_assign_compare()
   var lines =<< trim END
     vim9script
@@ -458,6 +483,7 @@ def Test_null_object_assign_compare()
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Test for object member initialization and disassembly
 def Test_class_member_initializer()
   var lines =<< trim END
       vim9script
@@ -517,32 +543,6 @@ def Test_member_any_used_as_object()
   END
   v9.CheckSourceSuccess(lines)
 
-  lines =<< trim END
-      vim9script
-
-      class Inner
-        this.value: number = 0
-      endclass
-
-      class Outer
-        this.inner: Inner
-      endclass
-
-      def F(outer: Outer)
-        outer.inner.value = 1
-      enddef
-
-      def Test_assign_to_nested_typed_member()
-        var inner = Inner.new(0)
-        var outer = Outer.new(inner)
-        F(outer)
-        assert_equal(1, inner.value)
-      enddef
-
-      Test_assign_to_nested_typed_member()
-  END
-  v9.CheckSourceSuccess(lines)
-
   # Try modifying a private variable using an "any" object
   lines =<< trim END
     vim9script
@@ -588,7 +588,37 @@ def Test_member_any_used_as_object()
   v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": 
someval')
 enddef
 
+" Nested assignment to a object variable which is of another class type
+def Test_assignment_nested_type()
+  var lines =<< trim END
+    vim9script
+
+    class Inner
+      public this.value: number = 0
+    endclass
+
+    class Outer
+      this.inner: Inner
+    endclass
+
+    def F(outer: Outer)
+      outer.inner.value = 1
+    enddef
+
+    def Test_assign_to_nested_typed_member()
+      var inner = Inner.new(0)
+      var outer = Outer.new(inner)
+      F(outer)
+      assert_equal(1, inner.value)
+    enddef
+
+    Test_assign_to_nested_typed_member()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
 def Test_assignment_with_operator()
+  # Use "+=" to assign to a object variable
   var lines =<< trim END
       vim9script
 
@@ -762,7 +792,6 @@ def Test_class_default_new()
   v9.CheckSourceFailure(lines, "E1328: Constructor default value must be 
v:none:  = 'a'")
 enddef
 
-
 def Test_class_new_with_object_member()
   var lines =<< trim END
       vim9script
@@ -1677,7 +1706,7 @@ func Test_interface_garbagecollect()
   call v9.CheckSourceSuccess(lines)
 endfunc
 
-def Test_class_function()
+def Test_class_method()
   var lines =<< trim END
       vim9script
       class Value
@@ -1713,6 +1742,29 @@ def Test_class_function()
     endclass
   END
   v9.CheckSourceFailure(lines, 'E1318:')
+
+  # Test for calling a class method from another class method without the class
+  # name prefix.
+  lines =<< trim END
+    vim9script
+    class A
+      static myList: list<number> = [1]
+      static def Foo(n: number)
+        myList->add(n)
+      enddef
+      static def Bar()
+        Foo(2)
+      enddef
+      def Baz()
+        Foo(3)
+      enddef
+    endclass
+    A.Bar()
+    var a = A.new()
+    a.Baz()
+    assert_equal([1, 2, 3], A.myList)
+  END
+  v9.CheckSourceSuccess(lines)
 enddef
 
 def Test_class_defcompile()
@@ -2343,7 +2395,6 @@ def Test_call_interface_method()
   END
   v9.CheckSourceSuccess(lines)
 
-
   # No class that implements the interface.
   lines =<< trim END
       vim9script
@@ -2685,7 +2736,6 @@ def Test_using_base_class()
   v9.CheckSourceSuccess(lines)
 enddef
 
-
 def Test_class_import()
   var lines =<< trim END
       vim9script
@@ -3651,7 +3701,7 @@ def Test_private_class_method()
         return 1234
       enddef
       def Bar()
-        assert_equal(1234, A._Foo())
+        assert_equal(1234, _Foo())
       enddef
     endclass
     var a = A.new()
@@ -3659,7 +3709,8 @@ def Test_private_class_method()
   END
   v9.CheckSourceSuccess(lines)
 
-  # Use a class private method from another class private method
+  # Use a class private method from another class private method without the
+  # class name prefix.
   lines =<< trim END
     vim9script
 
@@ -3668,10 +3719,10 @@ def Test_private_class_method()
         return 1234
       enddef
       static def _Foo2()
-        assert_equal(1234, A._Foo1())
+        assert_equal(1234, _Foo1())
       enddef
       def Bar()
-        A._Foo2()
+        _Foo2()
       enddef
     endclass
     var a = A.new()
@@ -4063,7 +4114,7 @@ def Test_private_member_access_outside_class()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a')
+  v9.CheckSourceFailure(lines, 'E1326: Member not found on object "A": _a')
 
   # private static member variable
   lines =<< trim END
@@ -4091,7 +4142,7 @@ def Test_private_member_access_outside_class()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only 
inside class "A"')
+  v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only 
using class "A"')
 
   # private static class variable
   lines =<< trim END
@@ -4265,7 +4316,7 @@ def Test_class_variable_access_using_object()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only 
inside class "A"')
+  v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only 
using class "A"')
 enddef
 
 " Test for using a interface method using a child object
@@ -4711,7 +4762,7 @@ def Test_class_variable()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only 
inside class "A"')
+  v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only 
using class "A"')
 
   # Reading a class variable using an object at function level
   lines =<< trim END
@@ -4977,7 +5028,7 @@ def Test_interface_with_unsupported_members()
       static num: number
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -4985,7 +5036,7 @@ def Test_interface_with_unsupported_members()
       static _num: number
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -4993,7 +5044,7 @@ def Test_interface_with_unsupported_members()
       public static num: number
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -5001,7 +5052,7 @@ def Test_interface_with_unsupported_members()
       public static _num: number
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -5009,7 +5060,7 @@ def Test_interface_with_unsupported_members()
       static def Foo(d: dict<any>): list<string>
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -5017,7 +5068,7 @@ def Test_interface_with_unsupported_members()
       static def _Foo(d: dict<any>): list<string>
     endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an 
interface')
 
   lines =<< trim END
     vim9script
@@ -5402,4 +5453,35 @@ def Test_implements_using_var_type_any()
   v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected 
list<dict<string>> but got dict<number>')
 enddef
 
+" Test for assigning to a member variable in a nested class
+def Test_nested_object_assignment()
+  var lines =<< trim END
+    vim9script
+
+    class A
+        this.value: number
+    endclass
+
+    class B
+        this.a: A = A.new()
+    endclass
+
+    class C
+        this.b: B = B.new()
+    endclass
+
+    class D
+        this.c: C = C.new()
+    endclass
+
+    def T(da: D)
+        da.c.b.a.value = 10
+    enddef
+
+    var d = D.new()
+    T(d)
+  END
+  v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "value"')
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 18c12fae5..7670cddde 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 */
+/**/
+    1909,
 /**/
     1908,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index 394589b02..da862f266 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -1592,7 +1592,7 @@ early_ret:
 
            if (!is_class)
            {
-               emsg(_(e_static_cannot_be_used_in_interface));
+               emsg(_(e_static_member_not_supported_in_interface));
                break;
            }
            has_static = TRUE;
@@ -1623,7 +1623,8 @@ early_ret:
            if (!is_class && *varname == '_')
            {
                // private variables are not supported in an interface
-               semsg(_(e_private_variable_str_in_interface), varname);
+               semsg(_(e_private_variable_not_supported_in_interface),
+                       varname);
                break;
            }
 
@@ -1689,7 +1690,8 @@ early_ret:
                if (!is_class && *name == '_')
                {
                    // private variables are not supported in an interface
-                   semsg(_(e_private_method_str_in_interface), name);
+                   semsg(_(e_private_method_not_supported_in_interface),
+                           name);
                    func_clear_free(uf, FALSE);
                    break;
                }
@@ -2010,8 +2012,7 @@ class_member_type(
     int                is_object,
     char_u     *name,
     char_u     *name_end,
-    int                *member_idx,
-    ocmember_T **p_m)
+    int                *member_idx)
 {
     size_t     len = name_end - name;
     ocmember_T *m;
@@ -2022,27 +2023,11 @@ class_member_type(
                                                                member_idx);
     if (m == NULL)
     {
-       char_u *varname = vim_strnsave(name, len);
-       if (varname != NULL)
-       {
-           if (is_object && class_member_idx(cl, name, len) >= 0)
-               // A class variable with this name is present
-               semsg(_(e_class_member_str_accessible_only_inside_class_str),
-                       varname, cl->class_name);
-           else if (!is_object && object_member_idx(cl, name, len) >= 0)
-               // An instance variable with this name is present
-               semsg(_(e_object_member_str_accessible_only_using_object_str),
-                       varname, cl->class_name);
-           else
-               semsg(_(e_unknown_variable_str), varname);
-       }
-       vim_free(varname);
+       member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
+                                                                       len);
        return &t_any;
     }
 
-    if (p_m != NULL)
-       *p_m = m;
-
     return m->ocm_type;
 }
 
@@ -2250,12 +2235,6 @@ class_object_index(
            semsg(_(e_cannot_access_private_member_str), m->ocm_name);
            return FAIL;
        }
-       if ((cl->class_flags & CLASS_INTERFACE) != 0)
-       {
-           semsg(_(e_interface_static_direct_access_str),
-                   cl->class_name, m->ocm_name);
-           return FAIL;
-       }
 
        typval_T *tv = &cl->class_members_tv[m_idx];
        copy_tv(tv, rettv);
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 7de1b6253..ab063f9ff 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1578,6 +1578,47 @@ is_decl_command(cmdidx_T cmdidx)
                                 || cmdidx == CMD_final || cmdidx == CMD_const;
 }
 
+/*
+ * Returns TRUE if the class or object variable in "lhs" is modifiable.
+ * "var_start" points to the start of the variable name and "lhs->lhs_varlen"
+ * has the total length.  Note that the "lhs" can be nested an object reference
+ * (e.g.  a.b.c.d.var).
+ */
+    static int
+lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
+{
+    size_t     varlen = lhs->lhs_varlen;
+    class_T    *cl = lhs->lhs_type->tt_class;
+    int                is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
+    char_u     *name = var_start + varlen + 1;
+    size_t     namelen = lhs->lhs_end - var_start - varlen - 1;
+    ocmember_T *m;
+
+    m = member_lookup(cl, lhs->lhs_type->tt_type, name, namelen, NULL);
+    if (m == NULL)
+    {
+       member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
+       return FALSE;
+    }
+
+    // If it is private member variable, then accessing it outside the
+    // class is not allowed.
+    // If it is a read only class variable, then it can be modified
+    // only inside the class where it is defined.
+    if ((m->ocm_access != VIM_ACCESS_ALL) &&
+           ((is_object && !inside_class(cctx, cl))
+            || (!is_object && cctx->ctx_ufunc->uf_class != cl)))
+    {
+       char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
+                               ? e_cannot_access_private_member_str
+                               : e_cannot_change_readonly_variable_str;
+       semsg(_(msg), m->ocm_name);
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
 /*
  * Figure out the LHS type and other properties for an assignment or one item
  * of ":unlet" with an index.
@@ -1691,9 +1732,9 @@ compile_lhs(
            {
                if (is_decl)
                {
-                   // if we come here with what looks like an assignment like 
.=
-                   // but which has been reject by assignment_len() from 
may_compile_assignment
-                   // give a better error message
+                   // if we come here with what looks like an assignment like
+                   // .= but which has been reject by assignment_len() from
+                   // may_compile_assignment give a better error message
                    char_u *p = skipwhite(lhs->lhs_end);
                    if (p[0] == '.' && p[1] == '=')
                        
emsg(_(e_dot_equal_not_supported_with_script_version_two));
@@ -1959,36 +2000,17 @@ compile_lhs(
        {
            // for an object or class member get the type of the member
            class_T     *cl = lhs->lhs_type->tt_class;
-           ocmember_T  *m;
            int         is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
 
+           if (!lhs_class_member_modifiable(lhs, var_start, cctx))
+               return FAIL;
+
            lhs->lhs_member_type = class_member_type(cl,
                                        is_object,
                                        after + 1, lhs->lhs_end,
-                                       &lhs->lhs_member_idx, &m);
+                                       &lhs->lhs_member_idx);
            if (lhs->lhs_member_idx < 0)
                return FAIL;
-           if ((cl->class_flags & CLASS_INTERFACE) != 0
-                                       && lhs->lhs_type->tt_type == VAR_CLASS)
-           {
-               semsg(_(e_interface_static_direct_access_str),
-                                               cl->class_name, m->ocm_name);
-               return FAIL;
-           }
-           // If it is private member variable, then accessing it outside the
-           // class is not allowed.
-           // If it is a read only class variable, then it can be modified
-           // only inside the class where it is defined.
-           if ((m->ocm_access != VIM_ACCESS_ALL) &&
-                   ((is_object && !inside_class(cctx, cl))
-                    || (!is_object && cctx->ctx_ufunc->uf_class != cl)))
-           {
-               char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
-                               ? e_cannot_access_private_member_str
-                               : e_cannot_change_readonly_variable_str;
-               semsg(_(msg), m->ocm_name);
-               return FAIL;
-           }
        }
        else
        {
@@ -2163,6 +2185,14 @@ compile_load_lhs(
 
        lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
                                                  : get_type_on_stack(cctx, 0);
+
+       if (lhs->lhs_type->tt_type == VAR_OBJECT)
+       {
+           // Check whether the object variable is modifiable
+           if (!lhs_class_member_modifiable(lhs, var_start, cctx))
+               return FAIL;
+       }
+
        // Now we can properly check the type.  The variable is indexed, thus
        // we need the member type.  For a class or object we don't know the
        // type yet, it depends on what member is used.
@@ -2198,8 +2228,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u 
*var_start, cctx_T *cctx)
 
        class_T *cl = lhs->lhs_type->tt_class;
        type_T  *type = class_member_type(cl, TRUE, dot + 1,
-                                          lhs->lhs_end, &lhs->lhs_member_idx,
-                                          NULL);
+                                         lhs->lhs_end, &lhs->lhs_member_idx);
        if (lhs->lhs_member_idx < 0)
            return FAIL;
 
diff --git a/src/vim9expr.c b/src/vim9expr.c
index a158f31ae..bcad79ca1 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -439,12 +439,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
        if (m != NULL)
        {
            // Note: type->tt_type = VAR_CLASS
-           if ((cl->class_flags & CLASS_INTERFACE) != 0)
-           {
-               semsg(_(e_interface_static_direct_access_str),
-                       cl->class_name, m->ocm_name);
-               return FAIL;
-           }
            // A private class variable can be accessed only in the class where
            // it is defined.
            if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
@@ -1152,9 +1146,6 @@ compile_call(
            // the class where the function is defined.
            if (cctx->ctx_ufunc->uf_defclass == cl)
            {
-               // The generate_CALL() function expects the class type at the
-               // top of the stack.  So push the class type to the stack.
-               push_type_stack(cctx, &t_class);
                res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
                                                        0, type, argcount);
            }
diff --git a/src/vim9instr.c b/src/vim9instr.c
index ccec0bcc3..b9bbfd09b 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1899,9 +1899,15 @@ generate_CALL(
     // drop the argument types
     cctx->ctx_type_stack.ga_len -= argcount;
 
-    // For an object or class method call, drop the object/class type
+    // For an object or class method call, drop the object/class type.
     if (ufunc->uf_class != NULL)
-       cctx->ctx_type_stack.ga_len--;
+    {
+       // When a class method is called without the class name prefix, then
+       // the type will not be in the stack.
+       type_T *stype = get_type_on_stack(cctx, 0);
+       if (stype->tt_type == VAR_CLASS || stype->tt_type == VAR_OBJECT)
+           cctx->ctx_type_stack.ga_len--;
+    }
 
     // add return type
     return push_type_stack(cctx, ufunc->uf_ret_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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1qiIXc-000hIJ-SE%40256bit.org.

Raspunde prin e-mail lui