diff --git a/src/errors.h b/src/errors.h index 6184037df8..1db70786b3 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1572,8 +1572,7 @@ EXTERN char e_unknown_printer_font_str[] #endif EXTERN char e_class_required[] INIT(= N_("E614: Class required")); -EXTERN char e_object_required[] - INIT(= N_("E615: Object required")); +// E615 unused EXTERN char e_object_required_for_argument_nr[] INIT(= N_("E616: Object required for argument %d")); #ifdef FEAT_GUI_GTK @@ -3401,8 +3400,7 @@ EXTERN char e_object_required_found_str[] INIT(= N_("E1327: Object required, found %s")); EXTERN char e_constructor_default_value_must_be_vnone_str[] INIT(= N_("E1328: Constructor default value must be v:none: %s")); -EXTERN char e_cannot_get_object_member_type_from_initializer_str[] - INIT(= N_("E1329: Cannot get object member type from initializer: %s")); +// E1329 unused EXTERN char e_invalid_type_for_object_member_str[] INIT(= N_("E1330: Invalid type for object member: %s")); EXTERN char e_public_must_be_followed_by_this_or_static[] @@ -3411,6 +3409,7 @@ EXTERN char e_public_member_name_cannot_start_with_underscore_str[] INIT(= N_("E1332: Public member name cannot start with underscore: %s")); EXTERN char e_cannot_access_private_member_str[] INIT(= N_("E1333: Cannot access private member: %s")); +// E1334 unused EXTERN char e_member_is_not_writable_str[] INIT(= N_("E1335: Member is not writable: %s")); #endif @@ -3419,8 +3418,8 @@ 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_member_not_found_on_class_str_str[] - INIT(= N_("E1338: Member not found on class \"%s\": %s")); +EXTERN char e_interface_static_direct_access_str[] + INIT(= N_("E1338: Cannot directly access interface \"%s\" static member \"%s\"")); #endif #ifdef FEAT_PROP_POPUP EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[] @@ -3444,9 +3443,9 @@ EXTERN char e_interface_name_not_found_str[] EXTERN char e_not_valid_interface_str[] INIT(= N_("E1347: Not a valid interface: %s")); EXTERN char e_member_str_of_interface_str_not_implemented[] - INIT(= N_("E1348: Member \"%s\" of interface \"%s\" not implemented")); -EXTERN char e_function_str_of_interface_str_not_implemented[] - INIT(= N_("E1349: Function \"%s\" of interface \"%s\" not implemented")); + INIT(= N_("E1348: Member \"%s\" of interface \"%s\" is not implemented")); +EXTERN char e_method_str_of_interface_str_not_implemented[] + INIT(= N_("E1349: Method \"%s\" of interface \"%s\" is not implemented")); EXTERN char e_duplicate_implements[] INIT(= N_("E1350: Duplicate \"implements\"")); EXTERN char e_duplicate_interface_after_implements_str[] @@ -3480,6 +3479,7 @@ EXTERN char e_incomplete_type[] #endif EXTERN char e_warning_pointer_block_corrupted[] INIT(= N_("E1364: Warning: Pointer block corrupted")); +#ifdef FEAT_EVAL EXTERN char e_cannot_use_a_return_type_with_new[] INIT(= N_("E1365: Cannot use a return type with the \"new\" function")); EXTERN char e_cannot_access_private_method_str[] @@ -3504,10 +3504,21 @@ EXTERN char e_class_member_str_accessible_only_using_class_str[] INIT(= N_("E1375: Class member \"%s\" accessible only using class \"%s\"")); 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_static_member_not_supported_in_interface[] - INIT(= N_("E1377: Static member is not supported in an interface")); EXTERN char e_method_str_of_class_str_has_different_access[] - INIT(= N_("E1378: Access level of method \"%s\" is different in class \"%s\"")); + 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[] + INIT(= N_("E1379: Private variable not supported in an interface")); +EXTERN char e_private_method_str_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\"")); +EXTERN char e_member_str_type_mismatch_expected_str_but_got_str[] + INIT(= N_("E1382: Member \"%s\": type mismatch, expected %s but got %s")); +EXTERN char e_method_str_type_mismatch_expected_str_but_got_str[] + INIT(= N_("E1383: Method \"%s\": type mismatch, expected %s but got %s")); +#endif EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] @@ -3520,12 +3531,6 @@ EXTERN char e_positional_arg_num_type_inconsistent_str_str[] INIT(= N_("E1404: Positional argument %d type used inconsistently: %s/%s")); EXTERN char e_invalid_format_specifier_str[] INIT(= N_("E1405: Invalid format specifier: %s")); -EXTERN char e_member_str_type_mismatch_expected_str_but_got_str[] - INIT(= N_("E1406: Member \"%s\": type mismatch, expected %s but got %s")); -EXTERN char e_method_str_type_mismatch_expected_str_but_got_str[] - INIT(= N_("E1407: Method \"%s\": type mismatch, expected %s but got %s")); EXTERN char e_aptypes_is_null_nr_str[] INIT(= "E1408: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"); -EXTERN char e_interface_static_direct_access_str[] - INIT(= N_("E1409: Cannot directly access interface \"%s\" static member \"%s\"")); -// E1376 - E1399 unused +// E1384 - E1399 unused diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index c793da6b48..1efc47a074 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -2339,6 +2339,25 @@ def Test_instanceof() Bar() END v9.CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected class but got number') + + lines =<< trim END + vim9script + class Foo + endclass + instanceof(Foo.new(), [{}]) + END + v9.CheckSourceFailure(lines, 'E614: Class required') + + lines =<< trim END + vim9script + class Foo + endclass + def Bar() + instanceof(Foo.new(), [{}]) + enddef + Bar() + END + v9.CheckSourceFailure(lines, 'E614: Class required') enddef def Test_invert() diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 1796f5f72e..5e3b945591 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -145,6 +145,65 @@ def Test_class_basic() END v9.CheckSourceFailure(lines, 'E1170:') + # Test for using class as a bool + lines =<< trim END + vim9script + class A + endclass + if A + endif + END + v9.CheckSourceFailure(lines, 'E1319: Using a class as a Number') + + # Test for using object as a bool + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + if a + endif + END + v9.CheckSourceFailure(lines, 'E1320: Using an object as a Number') + + # Test for using class as a float + lines =<< trim END + vim9script + class A + endclass + sort([1.1, A], 'f') + END + v9.CheckSourceFailure(lines, 'E1321: Using a class as a Float') + + # Test for using object as a float + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + sort([1.1, a], 'f') + END + v9.CheckSourceFailure(lines, 'E1322: Using an object as a Float') + + # Test for using class as a string + lines =<< trim END + vim9script + class A + endclass + :exe 'call ' .. A + END + v9.CheckSourceFailure(lines, 'E1323: Using a class as a String') + + # Test for using object as a string + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + :exe 'call ' .. a + END + v9.CheckSourceFailure(lines, 'E1324: Using an object as a String') + lines =<< trim END vim9script @@ -689,6 +748,18 @@ def Test_class_default_new() var missing = Person.new() END v9.CheckSourceFailure(lines, 'E119:') + + # Using a specific value to initialize an instance variable in the new() + # method. + lines =<< trim END + vim9script + class A + this.val: string + def new(this.val = 'a') + enddef + endclass + END + v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'") enddef @@ -825,6 +896,15 @@ def Test_class_object_member_inits() var a = A.new() END v9.CheckSourceFailure(lines, 'E1001:') + + # Test for initializing an object member with an special type + lines =<< trim END + vim9script + class A + this.value: void + endclass + END + v9.CheckSourceFailure(lines, 'E1330: Invalid type for object member: void') enddef " Test for instance variable access @@ -1556,17 +1636,10 @@ func Test_interface_garbagecollect() vim9script interface I - static ro_class_var: number - public static rw_class_var: number - static _priv_class_var: number this.ro_obj_var: number public this.rw_obj_var: number - this._priv_obj_var: number - static def ClassFoo(): number - static def _ClassBar(): number def ObjFoo(): number - def _ObjBar(): number endinterface class A implements I @@ -1736,8 +1809,8 @@ def Test_interface_basics() var lines =<< trim END vim9script interface Something - this.value: string - static count: number + this.ro_var: string + public this.rw_var: list def GetCount(): number endinterface END @@ -1750,16 +1823,6 @@ def Test_interface_basics() END v9.CheckSourceFailure(lines, 'E1342:') - lines =<< trim END - vim9script - - interface Some - static count: number - def Method(count: number) - endinterface - END - v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count', 5) - lines =<< trim END vim9script @@ -1784,7 +1847,7 @@ def Test_interface_basics() vim9script interface SomethingWrong this.value: string - static count = 7 + this.count = 7 def GetCount(): number endinterface END @@ -1794,7 +1857,7 @@ def Test_interface_basics() vim9script interface SomethingWrong this.value: string - static count: number + this.count: number def GetCount(): number return 5 enddef @@ -1845,12 +1908,12 @@ def Test_class_implements_interface() vim9script interface Some - static count: number + this.count: number def Method(nr: number) endinterface class SomeImpl implements Some - static count: number + this.count: number def Method(nr: number) echo nr enddef @@ -1862,7 +1925,7 @@ def Test_class_implements_interface() class AnotherImpl implements Some, Another this.member = 'abc' - static count: number + this.count = 20 def Method(nr: number) echo nr enddef @@ -1874,11 +1937,11 @@ def Test_class_implements_interface() vim9script interface Some - static counter: number + this.count: number endinterface class SomeImpl implements Some implements Some - static count: number + this.count: number endclass END v9.CheckSourceFailure(lines, 'E1350:') @@ -1887,11 +1950,11 @@ def Test_class_implements_interface() vim9script interface Some - static counter: number + this.count: number endinterface class SomeImpl implements Some, Some - static count: number + this.count: number endclass END v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some') @@ -1900,35 +1963,35 @@ def Test_class_implements_interface() vim9script interface Some - static counter: number + this.counter: number def Method(nr: number) endinterface class SomeImpl implements Some - static count: number + this.count: number def Method(nr: number) echo nr enddef endclass END - v9.CheckSourceFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented') + v9.CheckSourceFailure(lines, 'E1348: Member "counter" of interface "Some" is not implemented') lines =<< trim END vim9script interface Some - static count: number + this.count: number def Methods(nr: number) endinterface class SomeImpl implements Some - static count: number + this.count: number def Method(nr: number) echo nr enddef endclass END - v9.CheckSourceFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented') + v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented') # Check different order of members in class and interface works. lines =<< trim END @@ -2005,17 +2068,6 @@ def Test_class_implements_interface() END v9.CheckSourceFailure(lines, 'E1347:') - # all the class methods in an "interface" should be implemented - lines =<< trim END - vim9script - interface A - static def Foo() - endinterface - class B implements A - endclass - END - v9.CheckSourceFailure(lines, 'E1349:') - # implements should be followed by a white space lines =<< trim END vim9script @@ -2026,22 +2078,6 @@ def Test_class_implements_interface() END v9.CheckSourceFailure(lines, 'E1315:') - lines =<< trim END - vim9script - - interface One - static matching: bool - static as_any: any - static not_matching: number - endinterface - class Two implements One - static not_matching: string - static as_any: string - static matching: bool - endclass - END - v9.CheckSourceFailure(lines, 'E1406: Member "not_matching": type mismatch, expected number but got string') - lines =<< trim END vim9script @@ -2053,7 +2089,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string') + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string') lines =<< trim END vim9script @@ -2066,7 +2102,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool') + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool') lines =<< trim END vim9script @@ -2079,15 +2115,13 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool') + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool') # access superclass interface members from subclass, mix variable order lines =<< trim END vim9script interface I1 - public static svar1: number - public static svar2: number public this.mvar1: number public this.mvar2: number endinterface @@ -2140,15 +2174,11 @@ def Test_class_implements_interface() vim9script interface I1 - public static svar1: number - public static svar2: number public this.mvar1: number public this.mvar2: number endinterface interface I2 - public static svar3: number - public static svar4: number public this.mvar3: number public this.mvar4: number endinterface @@ -3747,146 +3777,6 @@ def Test_private_class_method() v9.CheckSourceFailure(lines, 'E1325: Method not found on class "C": _Foo') enddef -" Test for an interface private object_method -def Test_interface_private_object_method() - # Implement an interface private method and use it from a public method - var lines =<< trim END - vim9script - interface Intf - def _Foo(): number - endinterface - class A implements Intf - def _Foo(): number - return 1234 - enddef - def Bar(): number - return this._Foo() - enddef - endclass - var a = A.new() - assert_equal(1234, a.Bar()) - END - v9.CheckSourceSuccess(lines) - - # Call an interface private class method (script context) - lines =<< trim END - vim9script - interface Intf - def _Foo(): number - endinterface - class A implements Intf - def _Foo(): number - return 1234 - enddef - endclass - var a = A.new() - assert_equal(1234, a._Foo()) - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') - - # Call an interface private class method (def context) - lines =<< trim END - vim9script - interface Intf - def _Foo(): number - endinterface - class A implements Intf - def _Foo(): number - return 1234 - enddef - endclass - def T() - var a = A.new() - assert_equal(1234, a._Foo()) - enddef - T() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') - - # Implement an interface private object method as a private class method - lines =<< trim END - vim9script - interface Intf - def _Foo(): number - endinterface - class A implements Intf - static def _Foo(): number - return 1234 - enddef - endclass - END - v9.CheckSourceFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') -enddef - -" Test for an interface private class method -def Test_interface_private_class_method() - # Implement an interface private class method and use it from a public method - var lines =<< trim END - vim9script - interface Intf - static def _Foo(): number - endinterface - class A implements Intf - static def _Foo(): number - return 1234 - enddef - def Bar(): number - return A._Foo() - enddef - endclass - var a = A.new() - assert_equal(1234, a.Bar()) - END - v9.CheckSourceSuccess(lines) - - # Call an interface private class method (script context) - lines =<< trim END - vim9script - interface Intf - static def _Foo(): number - endinterface - class A implements Intf - static def _Foo(): number - return 1234 - enddef - endclass - assert_equal(1234, A._Foo()) - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo())') - - # Call an interface private class method (def context) - lines =<< trim END - vim9script - interface Intf - static def _Foo(): number - endinterface - class A implements Intf - static def _Foo(): number - return 1234 - enddef - endclass - def T() - assert_equal(1234, A._Foo()) - enddef - T() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo())') - - # Implement an interface private class method as a private object method - lines =<< trim END - vim9script - interface Intf - static def _Foo(): number - endinterface - class A implements Intf - def _Foo(): number - return 1234 - enddef - endclass - END - v9.CheckSourceFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') -enddef - " Test for using the return value of a class/object method as a function " argument. def Test_objmethod_funcarg() @@ -4142,124 +4032,6 @@ def Test_dup_member_variable() v9.CheckSourceSuccess(lines) enddef -def Test_interface_static_member_access() - # In a class cannot read from interface static - var lines =<< trim END - vim9script - interface I - public static num: number - endinterface - class C implements I - public static num = 3 - def F() - var x = I.num - enddef - endclass - C.new().F() - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') - - # In a class cannot write to interface static - lines =<< trim END - vim9script - interface I - public static num: number - endinterface - class C implements I - public static num = 3 - def F() - I.num = 7 - enddef - endclass - C.new().F() - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') - - # In a def cannot read from interface static - lines =<< trim END - vim9script - interface I - public static num: number - endinterface - def F() - var x = I.num - enddef - F() - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') - - # In a def cannot write to interface static - lines =<< trim END - vim9script - interface I - public static num: number - endinterface - def F() - I.num = 7 - enddef - F() - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') - - # script level cannot read interface static - lines =<< trim END - vim9script - interface I - public static s_var1: number - endinterface - - var x = I.s_var1 - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "s_var1"') - - # script level cannot write interface static - lines =<< trim END - vim9script - interface I - public static s_var1: number - endinterface - - I.s_var1 = 3 - END - v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "I.s_var1 = 3"') - -enddef - -def Test_static_member_access_outside_class() - # Verify access of statics implemented from interface - # in a :def (outside of a class) - # Note the order of the static is different - # between the interface and the class, - # since they are allocated in order in each interface/class; - # so the static index is mapped from interfaced to class as needed. - - # Check reading statics - var lines =<< trim END - vim9script - - interface I - public static s_var1: number - public static s_var2: number - endinterface - - class C implements I - public static s_var2 = 2 - public static x_static = 7 - public static s_var1 = 1 - endclass - - def F1(): number - assert_equal(1, C.s_var1) - assert_equal(2, C.s_var2) - assert_equal(7, C.x_static) - return 11 - enddef - - assert_equal(11, F1()) - END - v9.CheckSourceSuccess(lines) -enddef - " Test for accessing a private member outside a class in a def function def Test_private_member_access_outside_class() # private object member variable @@ -4627,9 +4399,11 @@ def Test_abstract_method() abstract def Foo() endinterface class B implements A + def Foo() + enddef endclass END - v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') + v9.CheckSourceSuccess(lines) # Abbreviate the "abstract" keyword lines =<< trim END @@ -4676,7 +4450,7 @@ def Test_abstract_method() enddef endclass END - v9.CheckSourceFailure(lines, 'E1407: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list') + v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list') # Use an abstract class to invoke an abstract method # FIXME: This should fail @@ -5149,7 +4923,7 @@ def Test_instance_method_access_level() enddef endclass END - v9.CheckSourceFailure(lines, 'E1378: Access level of method "_Foo" is different in class "A"') + v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"') # Public method in subclass lines =<< trim END @@ -5165,7 +4939,7 @@ def Test_instance_method_access_level() enddef endclass END - v9.CheckSourceFailure(lines, 'E1378: Access level of method "Foo" is different in class "A"') + v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"') enddef def Test_extend_empty_class() @@ -5194,4 +4968,438 @@ def Test_extend_empty_class() v9.CheckSourceSuccess(lines) enddef +" A interface cannot have a static variable or a static method or a private +" variable or a private method +def Test_interface_with_unsupported_members() + var lines =<< trim END + vim9script + interface A + static num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + static _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + public static num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + public static _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + static def Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + static def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') + + lines =<< trim END + vim9script + interface A + this._Foo: list + endinterface + END + v9.CheckSourceFailure(lines, 'E1379: Private variable not supported in an interface') + + lines =<< trim END + vim9script + interface A + def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1380: Private method not supported in an interface') +enddef + +" Test for extending an interface +def Test_extend_interface() + var lines =<< trim END + vim9script + interface A + this.var1: list + def Foo() + endinterface + interface B extends A + public this.var2: dict + def Bar() + endinterface + class C implements A, B + this.var1 = [1, 2] + def Foo() + enddef + public this.var2 = {a: '1'} + def Bar() + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + public this.var2: dict + endinterface + class C implements A, B + public this.var2 = {a: '1'} + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented') + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + public this.var2: dict + endinterface + class C implements A, B + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Member "var2" of interface "B" is not implemented') + + # interface cannot extend a class + lines =<< trim END + vim9script + class A + endclass + interface B extends A + endinterface + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A') + + # class cannot extend an interface + lines =<< trim END + vim9script + interface A + endinterface + class B extends A + endclass + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A') + + # interface cannot implement another interface + lines =<< trim END + vim9script + interface A + endinterface + interface B implements A + endinterface + END + v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"') + + # interface cannot extend multiple interfaces + lines =<< trim END + vim9script + interface A + endinterface + interface B + endinterface + interface C extends A, B + endinterface + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B') + + # Variable type in an extended interface is of different type + lines =<< trim END + vim9script + interface A + this.val1: number + endinterface + interface B extends A + this.val2: string + endinterface + interface C extends B + this.val1: string + this.val2: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1382: Member "val1": type mismatch, expected number but got string') +enddef + +" Test for a child class implementing an interface when some of the methods are +" defined in the parent class. +def Test_child_class_implements_interface() + var lines =<< trim END + vim9script + + interface Intf + def F1(): list> + def F2(): list> + def F3(): list> + this.var1: list> + this.var2: list> + this.var3: list> + endinterface + + class A + def A1() + enddef + def F3(): list> + return [[3]] + enddef + this.v1: list> = [[0]] + this.var3 = [{c: 30}] + endclass + + class B extends A + def B1() + enddef + def F2(): list> + return [[2]] + enddef + this.v2: list> = [[0]] + this.var2 = [{b: 20}] + endclass + + class C extends B implements Intf + def C1() + enddef + def F1(): list> + return [[1]] + enddef + this.v3: list> = [[0]] + this.var1 = [{a: 10}] + endclass + + def T(if: Intf) + assert_equal([[1]], if.F1()) + assert_equal([[2]], if.F2()) + assert_equal([[3]], if.F3()) + assert_equal([{a: 10}], if.var1) + assert_equal([{b: 20}], if.var2) + assert_equal([{c: 30}], if.var3) + enddef + + var c = C.new() + T(c) + assert_equal([[1]], c.F1()) + assert_equal([[2]], c.F2()) + assert_equal([[3]], c.F3()) + assert_equal([{a: 10}], c.var1) + assert_equal([{b: 20}], c.var2) + assert_equal([{c: 30}], c.var3) + END + v9.CheckSourceSuccess(lines) + + # One of the interface methods is not found + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented') + + # One of the interface methods is of different type + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def F3(): number + return 0 + enddef + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number') + + # One of the interface variables is not present + lines =<< trim END + vim9script + + interface Intf + this.var1: list> + this.var2: list> + this.var3: list> + endinterface + + class A + this.v1: list> = [[0]] + endclass + + class B extends A + this.v2: list> = [[0]] + this.var2 = [{b: 20}] + endclass + + class C extends B implements Intf + this.v3: list> = [[0]] + this.var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Member "var3" of interface "Intf" is not implemented') + + # One of the interface variables is of different type + lines =<< trim END + vim9script + + interface Intf + this.var1: list> + this.var2: list> + this.var3: list> + endinterface + + class A + this.v1: list> = [[0]] + this.var3: list> + endclass + + class B extends A + this.v2: list> = [[0]] + this.var2 = [{b: 20}] + endclass + + class C extends B implements Intf + this.v3: list> = [[0]] + this.var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1382: Member "var3": type mismatch, expected list> but got list>') +enddef + +" Test for extending an interface with duplicate variables and methods +def Test_interface_extends_with_dup_members() + var lines =<< trim END + vim9script + interface A + this.n1: number + def Foo1(): number + endinterface + interface B extends A + this.n2: number + this.n1: number + def Foo2(): number + def Foo1(): number + endinterface + class C implements B + this.n1 = 10 + this.n2 = 20 + def Foo1(): number + return 30 + enddef + def Foo2(): number + return 40 + enddef + endclass + def T1(a: A) + assert_equal(10, a.n1) + assert_equal(30, a.Foo1()) + enddef + def T2(b: B) + assert_equal(10, b.n1) + assert_equal(20, b.n2) + assert_equal(30, b.Foo1()) + assert_equal(40, b.Foo2()) + enddef + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using "any" type for a variable in a sub-class while it has a +" concrete type in the interface +def Test_implements_using_var_type_any() + var lines =<< trim END + vim9script + interface A + this.val: list> + endinterface + class B implements A + this.val = [{a: '1'}, {b: '2'}] + endclass + var b = B.new() + assert_equal([{a: '1'}, {b: '2'}], b.val) + END + v9.CheckSourceSuccess(lines) + + # initialize instance variable using a different type + lines =<< trim END + vim9script + interface A + this.val: list> + endinterface + class B implements A + this.val = {a: 1, b: 2} + endclass + var b = B.new() + END + v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list> but got dict') +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 540fd55fad..4cf4dfe5b0 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -3052,9 +3052,7 @@ def Test_disassemble_interface_static_member() var lines =<< trim END vim9script interface I - public static s_var: number public this.o_var: number - public static s_var2: number public this.o_var2: number endinterface diff --git a/src/version.c b/src/version.c index c9e2e2cc6b..f517b5b7e5 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 */ +/**/ + 1906, /**/ 1905, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index a36c482d16..394589b02b 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -293,7 +293,10 @@ object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl, * Returns TRUE if the class name "extends_names" is a valid class. */ static int -validate_extends_class(char_u *extends_name, class_T **extends_clp) +validate_extends_class( + char_u *extends_name, + class_T **extends_clp, + int is_class) { typval_T tv; int success = FALSE; @@ -305,9 +308,13 @@ validate_extends_class(char_u *extends_name, class_T **extends_clp) return success; } - if (tv.v_type != VAR_CLASS - || tv.vval.v_class == NULL - || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0) + if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL + || (is_class + && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0) + || (!is_class + && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)) + // a interface cannot extend a class and a class cannot extend an + // interface. semsg(_(e_cannot_extend_str), extends_name); else { @@ -352,6 +359,8 @@ validate_extends_methods( if (extends_private) pstr++; + // When comparing the method names, ignore the access type (public + // and private methods are considered the same). for (int j = 0; j < method_count; j++) { char_u *qstr = cl_fp[j]->uf_name; @@ -380,12 +389,10 @@ validate_extends_methods( * are no duplicates. */ static int -validate_extends_members( +extends_check_dup_members( garray_T *objmembers_gap, class_T *extends_cl) { - // loop == 1: check class members - // loop == 2: check object members int member_count = objmembers_gap->ga_len; if (member_count == 0) return TRUE; @@ -431,6 +438,68 @@ validate_extends_members( return TRUE; } +/* + * Compare the variable type of interface variables in "objmembers_gap" against + * the variable in any of the extended super interface lineage. Used to + * compare the variable types when extending interfaces. Returns TRUE if the + * variable types are the same. + */ + static int +extends_check_intf_var_type( + garray_T *objmembers_gap, + class_T *extends_cl) +{ + int member_count = objmembers_gap->ga_len; + if (member_count == 0) + return TRUE; + + ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data); + + // Validate each member variable + for (int c_i = 0; c_i < member_count; c_i++) + { + class_T *p_cl = extends_cl; + ocmember_T *c_m = members + c_i; + int var_found = FALSE; + + // Check in all the parent classes in the lineage + while (p_cl != NULL && !var_found) + { + int p_member_count = p_cl->class_obj_member_count; + if (p_member_count == 0) + { + p_cl = p_cl->class_extends; + continue; + } + ocmember_T *p_members = p_cl->class_obj_members; + + // Compare against all the members in the parent class + for (int p_i = 0; p_i < p_member_count; p_i++) + { + where_T where = WHERE_INIT; + ocmember_T *p_m = p_members + p_i; + + if (STRCMP(p_m->ocm_name, c_m->ocm_name) != 0) + continue; + + // Ensure the type is matching. + where.wt_func_name = (char *)c_m->ocm_name; + where.wt_kind = WT_MEMBER; + + if (check_type(p_m->ocm_type, c_m->ocm_type, TRUE, + where) == FAIL) + return FALSE; + + var_found = TRUE; + } + + p_cl = p_cl->class_extends; + } + } + + return TRUE; +} + /* * When extending an abstract class, check whether all the abstract methods in * the parent class are implemented. Returns TRUE if all the methods are @@ -491,60 +560,107 @@ validate_abstract_class_methods( } /* - * Check the members of the interface class "ifcl" match the class members - * ("classmembers_gap") and object members ("objmembers_gap") of a class. - * Returns TRUE if the class and object member names are valid. + * Returns TRUE if the interface variable "if_var" is present in the list of + * variables in "cl_mt" or in the parent lineage of one of the extended classes + * in "extends_cl". For a class variable, 'is_class_var' is TRUE. */ static int -validate_interface_members( +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) +{ + int variable_present = FALSE; + + for (int cl_i = 0; cl_i < cl_member_count; ++cl_i) + { + ocmember_T *m = &cl_mt[cl_i]; + where_T where = WHERE_INIT; + + if (STRCMP(if_var->ocm_name, m->ocm_name) != 0) + continue; + + // Ensure the access type is same + if (if_var->ocm_access != m->ocm_access) + { + semsg(_(e_member_str_of_interface_str_has_different_access), + if_var->ocm_name, intf_class_name); + return FALSE; + } + + // Ensure the type is matching. + if (m->ocm_type == &t_any) + { + // variable type is not specified. Use the variable type in the + // interface. + m->ocm_type = if_var->ocm_type; + } + else + { + where.wt_func_name = (char *)m->ocm_name; + where.wt_kind = WT_MEMBER; + if (check_type(if_var->ocm_type, m->ocm_type, TRUE, + where) == FAIL) + return FALSE; + } + + variable_present = TRUE; + break; + } + + 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; + return intf_variable_present(intf_class_name, if_var, + is_class_var, ext_cl_mt, + ext_cl_count, + extends_cl->class_extends); + } + + return 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. + */ + static int +validate_interface_variables( char_u *intf_class_name, class_T *ifcl, garray_T *classmembers_gap, - garray_T *objmembers_gap) + garray_T *objmembers_gap, + class_T *extends_cl) { for (int loop = 1; loop <= 2; ++loop) { - // loop == 1: check class members - // loop == 2: check object members - int if_count = loop == 1 ? ifcl->class_class_member_count + // 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 = loop == 1 ? ifcl->class_class_members + ocmember_T *if_ms = is_class_var ? ifcl->class_class_members : ifcl->class_obj_members; - ocmember_T *cl_ms = (ocmember_T *)(loop == 1 + ocmember_T *cl_ms = (ocmember_T *)(is_class_var ? classmembers_gap->ga_data : objmembers_gap->ga_data); - int cl_count = loop == 1 ? classmembers_gap->ga_len + 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) { - int cl_i; - for (cl_i = 0; cl_i < cl_count; ++cl_i) - { - ocmember_T *m = &cl_ms[cl_i]; - where_T where = WHERE_INIT; - - if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0) - continue; - - // Ensure the type is matching. - where.wt_func_name = (char *)m->ocm_name; - where.wt_kind = WT_MEMBER; - if (check_type(if_ms[if_i].ocm_type, m->ocm_type, TRUE, - where) == FAIL) - return FALSE; - - if (if_ms[if_i].ocm_access != m->ocm_access) - { - semsg(_(e_member_str_of_interface_str_has_different_access), - if_ms[if_i].ocm_name, intf_class_name); - return FALSE; - } - - break; - } - if (cl_i == cl_count) + if (!intf_variable_present(intf_class_name, &if_ms[if_i], + is_class_var, cl_ms, cl_count, extends_cl)) { semsg(_(e_member_str_of_interface_str_not_implemented), if_ms[if_i].ocm_name, intf_class_name); @@ -557,56 +673,107 @@ validate_interface_members( } /* - * Check the functions/methods of the interface class "ifcl" match the class - * methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a - * class. - * Returns TRUE if the class and object member names are valid. + * Returns TRUE if the method signature of "if_method" and "cl_method" matches. + */ + static int +intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method) +{ + where_T where = WHERE_INIT; + + // Ensure the type is matching. + where.wt_func_name = (char *)if_method->uf_name; + where.wt_kind = WT_METHOD; + if (check_type(if_method->uf_func_type, cl_method->uf_func_type, TRUE, + where) == FAIL) + return FALSE; + + return TRUE; +} + +/* + * Returns TRUE if the interface method "if_ufunc" is present in the list of + * methods in "cl_fp" or in the parent lineage of one of the extended classes + * in "extends_cl". For a class method, 'is_class_method' is TRUE. + */ + static int +intf_method_present( + ufunc_T *if_ufunc, + int is_class_method, + ufunc_T **cl_fp, + int cl_count, + class_T *extends_cl) +{ + int method_present = FALSE; + + for (int cl_i = 0; cl_i < cl_count; ++cl_i) + { + char_u *cl_name = cl_fp[cl_i]->uf_name; + if (STRCMP(if_ufunc->uf_name, cl_name) == 0) + { + // Ensure the type is matching. + if (!intf_method_type_matches(if_ufunc, cl_fp[cl_i])) + return FALSE; + method_present = TRUE; + break; + } + } + + 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); + } + + return method_present; +} + +/* + * Validate that a new class implements all the class/instance methods in the + * interface "ifcl". The new class methods are in "classfunctions_gap" and the + * new object methods are in "objmemthods_gap". Also validates the method + * types. + * Returns TRUE if all the interface class/object methods are implemented in + * the new class. */ static int validate_interface_methods( char_u *intf_class_name, class_T *ifcl, garray_T *classfunctions_gap, - garray_T *objmethods_gap) + garray_T *objmethods_gap, + class_T *extends_cl) { for (int loop = 1; loop <= 2; ++loop) { - // loop == 1: check class functions + // loop == 1: check class methods // loop == 2: check object methods - int if_count = loop == 1 ? ifcl->class_class_function_count + 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 = loop == 1 ? ifcl->class_class_functions + ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions : ifcl->class_obj_methods; - ufunc_T **cl_fp = (ufunc_T **)(loop == 1 + ufunc_T **cl_fp = (ufunc_T **)(is_class_method ? classfunctions_gap->ga_data : objmethods_gap->ga_data); - int cl_count = loop == 1 ? classfunctions_gap->ga_len + 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; - int cl_i; - for (cl_i = 0; cl_i < cl_count; ++cl_i) - { - char_u *cl_name = cl_fp[cl_i]->uf_name; - if (STRCMP(if_name, cl_name) == 0) - { - where_T where = WHERE_INIT; - // Ensure the type is matching. - where.wt_func_name = (char *)if_name; - where.wt_kind = WT_METHOD; - if (check_type(if_fp[if_i]->uf_func_type, - cl_fp[cl_i]->uf_func_type, TRUE, where) == FAIL) - return FALSE; - break; - } - } - if (cl_i == cl_count) + if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp, + cl_count, extends_cl)) { - semsg(_(e_function_str_of_interface_str_not_implemented), + semsg(_(e_method_str_of_interface_str_not_implemented), if_name, intf_class_name); return FALSE; } @@ -630,7 +797,8 @@ validate_implements_classes( garray_T *classfunctions_gap, garray_T *classmembers_gap, garray_T *objmethods_gap, - garray_T *objmembers_gap) + garray_T *objmembers_gap, + class_T *extends_cl) { int success = TRUE; @@ -660,15 +828,16 @@ validate_implements_classes( intf_classes[i] = ifcl; ++ifcl->class_refcount; - // check the members of the interface match the members of the class - success = validate_interface_members(impl, ifcl, classmembers_gap, - objmembers_gap); + // check the variables of the interface match the members of the class + success = validate_interface_variables(impl, ifcl, classmembers_gap, + 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); + classfunctions_gap, objmethods_gap, + extends_cl); clear_tv(&tv); } @@ -820,8 +989,7 @@ update_member_method_lookup_table( class_T *ifcl, class_T *cl, garray_T *objmethods, - int pobj_method_offset, - int is_interface) + int pobj_method_offset) { if (ifcl == NULL) return OK; @@ -876,7 +1044,7 @@ update_member_method_lookup_table( // extended class object method is not overridden by the child class. // Keep the method declared in one of the parent classes in the // lineage. - if (!done && !is_interface) + if (!done) { // If "ifcl" is not the immediate parent of "cl", then search in // the intermediate parent classes. @@ -927,13 +1095,20 @@ update_member_method_lookup_table( static int add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap) { + // update the lookup table for all the implemented interfaces for (int i = 0; i < cl->class_interface_count; ++i) { class_T *ifcl = cl->class_interfaces_cl[i]; - if (update_member_method_lookup_table(ifcl, cl, objmethods_gap, - 0, TRUE) == FAIL) - return FAIL; + // update the lookup table for this interface and all its super + // interfaces. + while (ifcl != NULL) + { + if (update_member_method_lookup_table(ifcl, cl, objmethods_gap, + 0) == FAIL) + return FAIL; + ifcl = ifcl->class_extends; + } } // Update the lookup table for the extended class, if any @@ -946,7 +1121,7 @@ add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap) while (pclass != NULL) { if (update_member_method_lookup_table(pclass, cl, - objmethods_gap, pobj_method_offset, FALSE) == FAIL) + objmethods_gap, pobj_method_offset) == FAIL) return FAIL; pobj_method_offset += pclass->class_obj_method_count_child; @@ -1237,6 +1412,12 @@ ex_class(exarg_T *eap) else if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10])) { + if (!is_class) + { + emsg(_(e_interface_cannot_use_implements)); + goto early_ret; + } + if (ga_impl.ga_len > 0) { emsg(_(e_duplicate_implements)); @@ -1377,18 +1558,25 @@ early_ret: break; } - if (!is_abstract) + if (!is_class) + // ignore "abstract" in an interface (as all the methods in an + // interface are abstract. + p = skipwhite(pa + 8); + else { - semsg(_(e_abstract_method_in_concrete_class), pa); - break; - } + if (!is_abstract) + { + semsg(_(e_abstract_method_in_concrete_class), pa); + break; + } - abstract_method = TRUE; - p = skipwhite(pa + 8); - if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0) - { - emsg(_(e_abstract_must_be_followed_by_def_or_static)); - break; + abstract_method = TRUE; + p = skipwhite(pa + 8); + if (STRNCMP(p, "def", 3) != 0 && STRNCMP(p, "static", 6) != 0) + { + emsg(_(e_abstract_must_be_followed_by_def_or_static)); + break; + } } } @@ -1401,6 +1589,12 @@ early_ret: semsg(_(e_command_cannot_be_shortened_str), ps); break; } + + if (!is_class) + { + emsg(_(e_static_cannot_be_used_in_interface)); + break; + } has_static = TRUE; p = skipwhite(ps + 6); } @@ -1425,6 +1619,14 @@ early_ret: char_u *varname_end = NULL; type_T *type = NULL; char_u *init_expr = NULL; + + if (!is_class && *varname == '_') + { + // private variables are not supported in an interface + semsg(_(e_private_variable_str_in_interface), varname); + break; + } + if (parse_member(eap, line, varname, has_public, &varname_end, &type_list, &type, is_class ? &init_expr: NULL) == FAIL) @@ -1484,6 +1686,13 @@ early_ret: char_u *name = uf->uf_name; int is_new = STRNCMP(name, "new", 3) == 0; + if (!is_class && *name == '_') + { + // private variables are not supported in an interface + semsg(_(e_private_method_str_in_interface), name); + func_clear_free(uf, FALSE); + break; + } if (is_new && !is_valid_constructor(uf, is_abstract, has_static)) { @@ -1562,7 +1771,7 @@ early_ret: // Check the "extends" class is valid. if (success && extends != NULL) - success = validate_extends_class(extends, &extends_cl); + success = validate_extends_class(extends, &extends_cl, is_class); VIM_CLEAR(extends); // Check the new object methods to make sure their access (public or @@ -1571,9 +1780,15 @@ early_ret: success = validate_extends_methods(&objmethods, extends_cl); // Check the new class and object variables are not duplicates of the - // variables in the extended class lineage. + // variables in the extended class lineage. If an interface is extending + // another interface, then it can duplicate the member variables. if (success && extends_cl != NULL) - success = validate_extends_members(&objmembers, extends_cl); + { + if (is_class) + success = extends_check_dup_members(&objmembers, extends_cl); + else + success = extends_check_intf_var_type(&objmembers, extends_cl); + } // When extending an abstract class, make sure all the abstract methods in // the parent class are implemented. If the current class is an abstract @@ -1592,7 +1807,8 @@ early_ret: success = validate_implements_classes(&ga_impl, intf_classes, &classfunctions, &classmembers, - &objmethods, &objmembers); + &objmethods, &objmembers, + extends_cl); } // Check no function argument name is used as a class member. @@ -2637,10 +2853,18 @@ class_instance_of(class_T *cl, class_T *other_cl) { if (cl == other_cl) return TRUE; - // Check the implemented interfaces. + // Check the implemented interfaces and the super interfaces for (int i = cl->class_interface_count - 1; i >= 0; --i) - if (cl->class_interfaces_cl[i] == other_cl) - return TRUE; + { + class_T *intf = cl->class_interfaces_cl[i]; + while (intf != NULL) + { + if (intf == other_cl) + return TRUE; + // check the super interfaces + intf = intf->class_extends; + } + } } return FALSE;