summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2023-09-18 19:56:49 +0200
committerChristian Brabandt <cb@256bit.org>2023-09-18 19:56:49 +0200
commit00cd18222ee1551c65228e9556c158624507fc7a (patch)
tree2686d05c81b58786e4f0c5b7f284f9e6d9158f8b /src
parentd25021cf036c63d539f845a1ee05b03ea21d61ff (diff)
patch 9.0.1909: Vim9: problem calling class method from other classv9.0.1909
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 <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Diffstat (limited to 'src')
-rw-r--r--src/errors.h11
-rw-r--r--src/eval.c8
-rw-r--r--src/proto/vim9class.pro2
-rw-r--r--src/testdir/test_vim9_class.vim172
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c37
-rw-r--r--src/vim9compile.c85
-rw-r--r--src/vim9expr.c9
-rw-r--r--src/vim9instr.c10
9 files changed, 208 insertions, 128 deletions
diff --git a/src/errors.h b/src/errors.h
index 1db70786b3..f1373a42c4 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 523546957e..0f952ee3e6 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 1448f19159..d09f654570 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 5e3b945591..fa621b25ac 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 18c12fae5f..7670cddde6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1909,
+/**/
1908,
/**/
1907,
diff --git a/src/vim9class.c b/src/vim9class.c
index 394589b02b..da862f2667 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 7de1b6253e..ab063f9ff2 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1579,6 +1579,47 @@ is_decl_command(cmdidx_T cmdidx)
}
/*
+ * 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.
* Returns OK or FAIL.
@@ -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 a158f31ae7..bcad79ca17 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 ccec0bcc38..b9bbfd09bd 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);