changeset 33506:f61713271934 v9.0.2002

patch 9.0.2002: Vim9: need cleanup of class related interface code Commit: https://github.com/vim/vim/commit/b852305dbf42f1206ecc6ae414fc200235fe2963 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Oct 8 19:07:39 2023 +0200 patch 9.0.2002: Vim9: need cleanup of class related interface code Problem: Vim9: need cleanup of class related interface code Solution: Remove the unused class variable and class method related code for interfaces. Remove unused class variable and class method related code for interfaces. Refactor the code. Optimize the object/class member double lookup in compile_lhs(). Change unused global functions to static functions. closes: #13302 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Sun, 08 Oct 2023 19:15:06 +0200
parents 398e729a5cd9
children acf5a30c2eac
files src/proto/vim9class.pro src/testdir/test_vim9_class.vim src/version.c src/vim9class.c src/vim9cmds.c src/vim9compile.c
diffstat 6 files changed, 267 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -10,11 +10,8 @@ ufunc_T *find_class_func(char_u **arg);
 int class_member_idx(class_T *cl, char_u *name, size_t namelen);
 ocmember_T *class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
 int class_method_idx(class_T *cl, char_u *name, size_t namelen);
-ufunc_T *class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
-int object_member_idx(class_T *cl, char_u *name, size_t namelen);
 ocmember_T *object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
 int object_method_idx(class_T *cl, char_u *name, size_t namelen);
-ufunc_T *object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx);
 ocmember_T *member_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
 void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
 ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
@@ -26,7 +23,6 @@ void class_unref(class_T *cl);
 int class_free_nonref(int copyID);
 int set_ref_in_classes(int copyID);
 void object_created(object_T *obj);
-void object_cleared(object_T *obj);
 int object_free_nonref(int copyID);
 void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
 void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -975,6 +975,28 @@ def Test_class_new_with_object_member()
     Check()
   END
   v9.CheckSourceSuccess(lines)
+
+  # Try using "this." argument in a class method
+  lines =<< trim END
+    vim9script
+    class A
+      this.val = 10
+      static def Foo(this.val: number)
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4)
+
+  # Try using "this." argument in an object method
+  lines =<< trim END
+    vim9script
+    class A
+      this.val = 10
+      def Foo(this.val: number)
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4)
 enddef
 
 def Test_class_object_member_inits()
@@ -1722,7 +1744,7 @@ def Test_class_member()
     var a = A.new()
     var v = a.bar
   END
-  v9.CheckSourceFailure(lines, 'E1326: Variable not found on object "A": bar', 5)
+  v9.CheckSourceFailure(lines, 'E1337: Class variable "bar" not found in class "A"', 5)
 enddef
 
 " These messages should show the defining class of the variable (base class),
@@ -4255,7 +4277,7 @@ def Test_private_object_method()
     var a = A.new()
     a._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 9)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 9)
 
   # Try calling a private method using an object (from a def function)
   lines =<< trim END
@@ -4468,7 +4490,7 @@ def Test_private_object_method()
     var c = C.new()
     assert_equal(1234, c._Foo())
   END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 16)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 16)
 
   # Using "_" prefix in a method name should fail outside of a class
   lines =<< trim END
@@ -4494,7 +4516,7 @@ def Test_private_class_method()
     endclass
     A._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 8)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 8)
 
   # Try calling a class private method (from a def function)
   lines =<< trim END
@@ -5122,7 +5144,7 @@ def Test_class_variable_access_using_obj
     var a = A.new()
     echo a.svar2
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+  v9.CheckSourceFailure(lines, 'E1337: Class variable "svar2" not found in class "A"', 8)
 
   # Cannot write to a class variable using an object in script context
   lines =<< trim END
@@ -5597,7 +5619,7 @@ def Test_class_variable()
     var a = A.new()
     var i = a.val
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
+  v9.CheckSourceFailure(lines, 'E1337: Class variable "val" not found in class "A"', 7)
 
   # Modifying a class variable using an object at function level
   lines =<< trim END
@@ -5969,6 +5991,18 @@ def Test_extend_interface()
   END
   v9.CheckSourceSuccess(lines)
 
+  # extending empty interface
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    interface B extends A
+    endinterface
+    class C implements B
+    endclass
+  END
+  v9.CheckSourceSuccess(lines)
+
   lines =<< trim END
     vim9script
     interface A
@@ -6567,6 +6601,17 @@ def Test_reserved_varname()
       o.F()
     END
     v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+
+    # class variable name
+    if kword != 'this'
+      lines =<< trim eval END
+        vim9script
+        class C
+          public static {kword}: list<number> = [1, 2, 3]
+        endclass
+      END
+      v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3)
+    endif
   endfor
 enddef
 
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2002,
+/**/
     2001,
 /**/
     2000,
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -555,7 +555,6 @@ validate_abstract_class_methods(
 intf_variable_present(
     char_u	*intf_class_name,
     ocmember_T *if_var,
-    int		is_class_var,
     ocmember_T *cl_mt,
     int		cl_member_count,
     class_T	*extends_cl)
@@ -600,15 +599,10 @@ intf_variable_present(
 
     if (!variable_present && extends_cl != NULL)
     {
-	int ext_cl_count = is_class_var
-				? extends_cl->class_class_member_count
-				: extends_cl->class_obj_member_count;
-	ocmember_T *ext_cl_mt = is_class_var
-				? extends_cl->class_class_members
-				: extends_cl->class_obj_members;
+	int ext_cl_count = extends_cl->class_obj_member_count;
+	ocmember_T *ext_cl_mt = extends_cl->class_obj_members;
 	return intf_variable_present(intf_class_name, if_var,
-					is_class_var, ext_cl_mt,
-					ext_cl_count,
+					ext_cl_mt, ext_cl_count,
 					extends_cl->class_extends);
     }
 
@@ -616,43 +610,32 @@ intf_variable_present(
 }
 
 /*
- * Check the variables of the interface class "ifcl" match the class variables
- * ("classmembers_gap") and object variables ("objmembers_gap") of a class.
- * Returns TRUE if the class and object variables names are valid.
+ * Check the variables of the interface class "ifcl" match object variables
+ * ("objmembers_gap") of a class.
+ * Returns TRUE if the object variables names are valid.
  */
     static int
 validate_interface_variables(
     char_u	*intf_class_name,
     class_T	*ifcl,
-    garray_T	*classmembers_gap,
     garray_T	*objmembers_gap,
     class_T	*extends_cl)
 {
-    for (int loop = 1; loop <= 2; ++loop)
+    int if_count = ifcl->class_obj_member_count;
+    if (if_count == 0)
+	return TRUE;
+
+    ocmember_T *if_ms = ifcl->class_obj_members;
+    ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data);
+    int cl_count = objmembers_gap->ga_len;
+    for (int if_i = 0; if_i < if_count; ++if_i)
     {
-	// loop == 1: check class variables
-	// loop == 2: check object variables
-	int is_class_var = (loop == 1);
-	int if_count = is_class_var ? ifcl->class_class_member_count
-						: ifcl->class_obj_member_count;
-	if (if_count == 0)
-	    continue;
-	ocmember_T *if_ms = is_class_var ? ifcl->class_class_members
-						: ifcl->class_obj_members;
-	ocmember_T *cl_ms = (ocmember_T *)(is_class_var
-						? classmembers_gap->ga_data
-						: objmembers_gap->ga_data);
-	int cl_count = is_class_var ? classmembers_gap->ga_len
-						: objmembers_gap->ga_len;
-	for (int if_i = 0; if_i < if_count; ++if_i)
+	if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms,
+							cl_count, extends_cl))
 	{
-	    if (!intf_variable_present(intf_class_name, &if_ms[if_i],
-				is_class_var, cl_ms, cl_count, extends_cl))
-	    {
-		semsg(_(e_variable_str_of_interface_str_not_implemented),
-			if_ms[if_i].ocm_name, intf_class_name);
-		return FALSE;
-	    }
+	    semsg(_(e_variable_str_of_interface_str_not_implemented),
+		    if_ms[if_i].ocm_name, intf_class_name);
+	    return FALSE;
 	}
     }
 
@@ -685,7 +668,6 @@ intf_method_type_matches(ufunc_T *if_met
     static int
 intf_method_present(
     ufunc_T *if_ufunc,
-    int	    is_class_method,
     ufunc_T **cl_fp,
     int	    cl_count,
     class_T *extends_cl)
@@ -707,15 +689,10 @@ intf_method_present(
 
     if (!method_present && extends_cl != NULL)
     {
-	ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method
-					? extends_cl->class_class_functions
-					: extends_cl->class_obj_methods);
-	int	ext_cl_count = is_class_method
-				? extends_cl->class_class_function_count
-				: extends_cl->class_obj_method_count;
-	return intf_method_present(if_ufunc, is_class_method, ext_cl_fp,
-					ext_cl_count,
-					extends_cl->class_extends);
+	ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods);
+	int	ext_cl_count = extends_cl->class_obj_method_count;
+	return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count,
+						extends_cl->class_extends);
     }
 
     return method_present;
@@ -733,37 +710,25 @@ intf_method_present(
 validate_interface_methods(
     char_u	*intf_class_name,
     class_T	*ifcl,
-    garray_T	*classfunctions_gap,
     garray_T	*objmethods_gap,
     class_T	*extends_cl)
 {
-    for (int loop = 1; loop <= 2; ++loop)
+    int if_count = ifcl->class_obj_method_count;
+    if (if_count == 0)
+	return TRUE;
+
+    ufunc_T **if_fp = ifcl->class_obj_methods;
+    ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data);
+    int cl_count = objmethods_gap->ga_len;
+    for (int if_i = 0; if_i < if_count; ++if_i)
     {
-	// loop == 1: check class methods
-	// loop == 2: check object methods
-	int is_class_method = (loop == 1);
-	int if_count = is_class_method ? ifcl->class_class_function_count
-					: ifcl->class_obj_method_count;
-	if (if_count == 0)
-	    continue;
-	ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions
-						: ifcl->class_obj_methods;
-	ufunc_T **cl_fp = (ufunc_T **)(is_class_method
-						? classfunctions_gap->ga_data
-						: objmethods_gap->ga_data);
-	int cl_count = is_class_method ? classfunctions_gap->ga_len
-						: objmethods_gap->ga_len;
-	for (int if_i = 0; if_i < if_count; ++if_i)
+	char_u	*if_name = if_fp[if_i]->uf_name;
+
+	if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl))
 	{
-	    char_u	*if_name = if_fp[if_i]->uf_name;
-
-	    if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp,
-							cl_count, extends_cl))
-	    {
-		semsg(_(e_method_str_of_interface_str_not_implemented),
-			if_name, intf_class_name);
-		return FALSE;
-	    }
+	    semsg(_(e_method_str_of_interface_str_not_implemented),
+		    if_name, intf_class_name);
+	    return FALSE;
 	}
     }
 
@@ -781,8 +746,6 @@ validate_interface_methods(
 validate_implements_classes(
     garray_T	*impl_gap,
     class_T	**intf_classes,
-    garray_T	*classfunctions_gap,
-    garray_T	*classmembers_gap,
     garray_T	*objmethods_gap,
     garray_T	*objmembers_gap,
     class_T	*extends_cl)
@@ -816,15 +779,14 @@ validate_implements_classes(
 	++ifcl->class_refcount;
 
 	// check the variables of the interface match the members of the class
-	success = validate_interface_variables(impl, ifcl, classmembers_gap,
-						objmembers_gap, extends_cl);
+	success = validate_interface_variables(impl, ifcl, objmembers_gap,
+								extends_cl);
 
 	// check the functions/methods of the interface match the
 	// functions/methods of the class
 	if (success)
-	    success = validate_interface_methods(impl, ifcl,
-					classfunctions_gap, objmethods_gap,
-					extends_cl);
+	    success = validate_interface_methods(impl, ifcl, objmethods_gap,
+								extends_cl);
 	clear_tv(&tv);
     }
 
@@ -1873,9 +1835,7 @@ early_ret:
 	intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
 
 	success = validate_implements_classes(&ga_impl, intf_classes,
-					&classfunctions, &classmembers,
-					&objmethods, &objmembers,
-					extends_cl);
+					&objmethods, &objmembers, extends_cl);
     }
 
     // Check no function argument name is used as a class member.
@@ -2172,18 +2132,90 @@ get_member_tv(
 	return FAIL;
     }
 
-    // The object only contains a pointer to the class, the member
-    // values array follows right after that.
-    object_T *obj = rettv->vval.v_object;
     if (is_object)
     {
+	// The object only contains a pointer to the class, the member values
+	// array follows right after that.
+	object_T *obj = rettv->vval.v_object;
 	typval_T *tv = (typval_T *)(obj + 1) + m_idx;
 	copy_tv(tv, rettv);
+	object_unref(obj);
     }
     else
+    {
 	copy_tv(&cl->class_members_tv[m_idx], rettv);
+	class_unref(cl);
+    }
+
+    return OK;
+}
+
+/*
+ * Call an object or class method "name" in class "cl".  The method return
+ * value is returned in "rettv".
+ */
+    static int
+call_oc_method(
+    class_T	*cl,
+    char_u	*name,
+    size_t	len,
+    char_u	*name_end,
+    evalarg_T	*evalarg,
+    char_u	**arg,
+    typval_T	*rettv)
+{
+    ufunc_T	*fp;
+    typval_T	argvars[MAX_FUNC_ARGS + 1];
+    int		argcount = 0;
+
+    fp = method_lookup(cl, rettv->v_type, name, len, NULL);
+    if (fp == NULL)
+    {
+	method_not_found_msg(cl, rettv->v_type, name, len);
+	return FAIL;
+    }
 
-    object_unref(obj);
+    if (*fp->uf_name == '_')
+    {
+	// Cannot access a private method outside of a class
+	semsg(_(e_cannot_access_private_method_str), fp->uf_name);
+	return FAIL;
+    }
+
+    char_u *argp = name_end;
+    int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount);
+    if (ret == FAIL)
+	return FAIL;
+
+    funcexe_T funcexe;
+    CLEAR_FIELD(funcexe);
+    funcexe.fe_evaluate = TRUE;
+    if (rettv->v_type == VAR_OBJECT)
+    {
+	funcexe.fe_object = rettv->vval.v_object;
+	++funcexe.fe_object->obj_refcount;
+    }
+
+    // Clear the class or object after calling the function, in
+    // case the refcount is one.
+    typval_T tv_tofree = *rettv;
+    rettv->v_type = VAR_UNKNOWN;
+
+    // Call the user function.  Result goes into rettv;
+    int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe,
+								NULL);
+
+    // Clear the previous rettv and the arguments.
+    clear_tv(&tv_tofree);
+    for (int idx = 0; idx < argcount; ++idx)
+	clear_tv(&argvars[idx]);
+
+    if (error != FCERR_NONE)
+    {
+	user_func_error(error, printable_func_name(fp), funcexe.fe_found_var);
+	return FAIL;
+    }
+    *arg = argp;
 
     return OK;
 }
@@ -2242,104 +2274,22 @@ class_object_index(
     }
 
     if (*name_end == '(')
-    {
-	ufunc_T *fp;
-
-	fp = method_lookup(cl, rettv->v_type, name, len, NULL);
-	if (fp == NULL)
-	{
-	    method_not_found_msg(cl, rettv->v_type, name, len);
-	    return FAIL;
-	}
-
-	typval_T	argvars[MAX_FUNC_ARGS + 1];
-	int		argcount = 0;
-
-	if (*fp->uf_name == '_')
-	{
-	    // Cannot access a private method outside of a class
-	    semsg(_(e_cannot_access_private_method_str), name);
-	    return FAIL;
-	}
-
-	char_u *argp = name_end;
-	int ret = get_func_arguments(&argp, evalarg, 0,
-		argvars, &argcount);
-	if (ret == FAIL)
-	    return FAIL;
+	// Invoke the class or object method
+	return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv);
 
-	funcexe_T funcexe;
-	CLEAR_FIELD(funcexe);
-	funcexe.fe_evaluate = TRUE;
-	if (rettv->v_type == VAR_OBJECT)
-	{
-	    funcexe.fe_object = rettv->vval.v_object;
-	    ++funcexe.fe_object->obj_refcount;
-	}
-
-	// Clear the class or object after calling the function, in
-	// case the refcount is one.
-	typval_T tv_tofree = *rettv;
-	rettv->v_type = VAR_UNKNOWN;
-
-	// Call the user function.  Result goes into rettv;
-	int error = call_user_func_check(fp, argcount, argvars,
-		rettv, &funcexe, NULL);
-
-	// Clear the previous rettv and the arguments.
-	clear_tv(&tv_tofree);
-	for (int idx = 0; idx < argcount; ++idx)
-	    clear_tv(&argvars[idx]);
-
-	if (error != FCERR_NONE)
-	{
-	    user_func_error(error, printable_func_name(fp),
-		    funcexe.fe_found_var);
-	    return FAIL;
-	}
-	*arg = argp;
-	return OK;
-    }
-
-    else if (rettv->v_type == VAR_OBJECT)
+    else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS)
     {
 	// Search in the object member variable table and the class member
 	// variable table.
-	if (get_member_tv(cl, TRUE, name, len, rettv) == OK)
+	int is_object = rettv->v_type == VAR_OBJECT;
+	if (get_member_tv(cl, is_object, name, len, rettv) == OK)
 	{
 	    *arg = name_end;
 	    return OK;
 	}
 
 	if (did_emsg == did_emsg_save)
-	    member_not_found_msg(cl, VAR_OBJECT, name, len);
-    }
-
-    else if (rettv->v_type == VAR_CLASS)
-    {
-	int	    m_idx;
-
-	// class member
-	ocmember_T *m = class_member_lookup(cl, name, len, &m_idx);
-	if (m == NULL)
-	{
-	    member_not_found_msg(cl, VAR_CLASS, name, len);
-	    return FAIL;
-	}
-
-	if (*name == '_')
-	{
-	    emsg_var_cl_define(e_cannot_access_private_variable_str,
-							m->ocm_name, 0, cl);
-	    return FAIL;
-	}
-
-	typval_T *tv = &cl->class_members_tv[m_idx];
-	copy_tv(tv, rettv);
-	class_unref(cl);
-
-	*arg = name_end;
-	return OK;
+	    member_not_found_msg(cl, is_object, name, len);
     }
 
     return FAIL;
@@ -2433,23 +2383,11 @@ class_member_lookup(class_T *cl, char_u 
 }
 
 /*
- * Returns the index of class method "name" in the class "cl".
- * Returns -1, if the method is not found.
- */
-    int
-class_method_idx(class_T *cl, char_u *name, size_t namelen)
-{
-    int idx;
-    class_method_lookup(cl, name, namelen, &idx);
-    return idx;
-}
-
-/*
  * Returns a pointer to the class method "name" in class "cl".
  * Returns NULL if the method is not found.
  * The method index is set in "idx".
  */
-    ufunc_T *
+    static ufunc_T *
 class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
 {
     ufunc_T	*ret_fp = NULL;
@@ -2471,11 +2409,23 @@ class_method_lookup(class_T *cl, char_u 
 }
 
 /*
+ * Returns the index of class method "name" in the class "cl".
+ * Returns -1, if the method is not found.
+ */
+    int
+class_method_idx(class_T *cl, char_u *name, size_t namelen)
+{
+    int idx;
+    class_method_lookup(cl, name, namelen, &idx);
+    return idx;
+}
+
+/*
  * Returns the index of object member variable "name" in the class "cl".
  * Returns -1, if the variable is not found.
  * If "namelen" is zero, then it is assumed that "name" is NUL terminated.
  */
-    int
+    static int
 object_member_idx(class_T *cl, char_u *name, size_t namelen)
 {
     int idx;
@@ -2519,23 +2469,11 @@ object_member_lookup(class_T *cl, char_u
 }
 
 /*
- * Returns the index of object method "name" in the class "cl".
- * Returns -1, if the method is not found.
- */
-    int
-object_method_idx(class_T *cl, char_u *name, size_t namelen)
-{
-    int idx;
-    object_method_lookup(cl, name, namelen, &idx);
-    return idx;
-}
-
-/*
  * Returns a pointer to the object method "name" in class "cl".
  * Returns NULL if the method is not found.
  * The object method index is set in "idx".
  */
-    ufunc_T *
+    static ufunc_T *
 object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx)
 {
     ufunc_T	*ret_fp = NULL;
@@ -2559,6 +2497,18 @@ object_method_lookup(class_T *cl, char_u
 }
 
 /*
+ * Returns the index of object method "name" in the class "cl".
+ * Returns -1, if the method is not found.
+ */
+    int
+object_method_idx(class_T *cl, char_u *name, size_t namelen)
+{
+    int idx;
+    object_method_lookup(cl, name, namelen, &idx);
+    return idx;
+}
+
+/*
  * Lookup a class or object member variable by name.  If v_type is VAR_CLASS,
  * then lookup a class member variable and if it is VAR_OBJECT, then lookup a
  * object member variable.
@@ -2682,42 +2632,6 @@ copy_object(typval_T *from, typval_T *to
 }
 
 /*
- * Free an object.
- */
-    static void
-object_clear(object_T *obj)
-{
-    // Avoid a recursive call, it can happen if "obj" has a circular reference.
-    obj->obj_refcount = INT_MAX;
-
-    class_T *cl = obj->obj_class;
-
-    if (!cl)
-	return;
-
-    // the member values are just after the object structure
-    typval_T *tv = (typval_T *)(obj + 1);
-    for (int i = 0; i < cl->class_obj_member_count; ++i)
-	clear_tv(tv + i);
-
-    // Remove from the list headed by "first_object".
-    object_cleared(obj);
-
-    vim_free(obj);
-    class_unref(cl);
-}
-
-/*
- * Unreference an object.
- */
-    void
-object_unref(object_T *obj)
-{
-    if (obj != NULL && --obj->obj_refcount <= 0)
-	object_clear(obj);
-}
-
-/*
  * Make a copy of a class.
  */
     void
@@ -2866,7 +2780,7 @@ static object_T	*next_nonref_obj = NULL;
  * Call this function when an object has been cleared and is about to be freed.
  * It is removed from the list headed by "first_object".
  */
-    void
+    static void
 object_cleared(object_T *obj)
 {
     if (obj->obj_next_used != NULL)
@@ -2882,6 +2796,42 @@ object_cleared(object_T *obj)
 }
 
 /*
+ * Free an object.
+ */
+    static void
+object_clear(object_T *obj)
+{
+    // Avoid a recursive call, it can happen if "obj" has a circular reference.
+    obj->obj_refcount = INT_MAX;
+
+    class_T *cl = obj->obj_class;
+
+    if (!cl)
+	return;
+
+    // the member values are just after the object structure
+    typval_T *tv = (typval_T *)(obj + 1);
+    for (int i = 0; i < cl->class_obj_member_count; ++i)
+	clear_tv(tv + i);
+
+    // Remove from the list headed by "first_object".
+    object_cleared(obj);
+
+    vim_free(obj);
+    class_unref(cl);
+}
+
+/*
+ * Unreference an object.
+ */
+    void
+object_unref(object_T *obj)
+{
+    if (obj != NULL && --obj->obj_refcount <= 0)
+	object_clear(obj);
+}
+
+/*
  * Go through the list of all objects and free items without "copyID".
  */
     int
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -254,7 +254,7 @@ compile_lock_unlock(
 		{
 		    // Push the class of the bare class variable name
 		    name = cl->class_name;
-		    len = STRLEN(name);
+		    len = (int)STRLEN(name);
 #ifdef LOG_LOCKVAR
 		    ch_log(NULL, "LKVAR:    ... cctx_class_member: name %s",
 			   name);
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2011,16 +2011,33 @@ compile_lhs(
 	    // for an object or class member get the type of the member
 	    class_T	*cl = lhs->lhs_type->tt_class;
 	    int		is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
-
-	    if (!lhs_class_member_modifiable(lhs, var_start, cctx))
+	    char_u	*name = var_start + lhs->lhs_varlen + 1;
+	    size_t	namelen = lhs->lhs_end - var_start - lhs->lhs_varlen - 1;
+
+	    ocmember_T	*m = member_lookup(cl, lhs->lhs_type->tt_type,
+					name, namelen, &lhs->lhs_member_idx);
+	    if (m == NULL)
+	    {
+		member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
 		return FAIL;
-
-	    lhs->lhs_member_type = class_member_type(cl,
-					is_object,
-					after + 1, lhs->lhs_end,
-					&lhs->lhs_member_idx);
-	    if (lhs->lhs_member_idx < 0)
+	    }
+
+	    // 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_variable_str
+					: e_variable_is_not_writable_str;
+		emsg_var_cl_define(msg, m->ocm_name, 0, cl);
 		return FAIL;
+	    }
+
+	    lhs->lhs_member_type = m->ocm_type;
 	}
 	else
 	{