patch 9.1.1012: Vim9: class interface inheritance not correctly working
Problem: Vim9: class interface inheritance not correctly working
Solution: make the class inherit the interfaces of the super class
fixes: #16395
closes: #16412
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index c20de25..23281bc 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -6096,44 +6096,151 @@
" Test for using an interface method using a child object when it is overridden
" by the child class.
-" FIXME: This test fails.
-" def Test_interface_overridden_method_from_child()
-" var lines =<< trim END
-" vim9script
-"
-" interface A
-" def Foo(): string
-" endinterface
-"
-" class B implements A
-" def Foo(): string
-" return 'b-foo'
-" enddef
-" endclass
-"
-" class C extends B
-" def Bar(): string
-" return 'bar'
-" enddef
-" def Foo(): string
-" return 'c-foo'
-" enddef
-" endclass
-"
-" def T1(a: A)
-" assert_equal('c-foo', a.Foo())
-" enddef
-"
-" def T2(b: B)
-" assert_equal('c-foo', b.Foo())
-" enddef
-"
-" var c = C.new()
-" T1(c)
-" T2(c)
-" END
-" v9.CheckSourceSuccess(lines)
-" enddef
+def Test_interface_overridden_method_from_child()
+ var lines =<< trim END
+ vim9script
+
+ interface A
+ def Foo(): string
+ endinterface
+
+ class B implements A
+ def Foo(): string
+ return 'b-foo'
+ enddef
+ endclass
+
+ class C extends B
+ def Bar(): string
+ return 'bar'
+ enddef
+ def Foo(): string
+ return 'c-foo'
+ enddef
+ endclass
+
+ def T1(a: A)
+ assert_equal('c-foo', a.Foo())
+ enddef
+
+ def T2(b: B)
+ assert_equal('c-foo', b.Foo())
+ enddef
+
+ var c = C.new()
+ T1(c)
+ T2(c)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for interface inheritance
+def Test_interface_inheritance()
+ var lines =<< trim END
+ vim9script
+
+ interface A
+ def A_Fn(): string
+ endinterface
+
+ interface B
+ def B_Fn(): string
+ endinterface
+
+ interface C
+ def C_Fn(): string
+ endinterface
+
+ class C1 implements A
+ def A_Fn(): string
+ return 'c1-a'
+ enddef
+ endclass
+
+ class C2 extends C1 implements B
+ def B_Fn(): string
+ return 'c2-b'
+ enddef
+ def A_Fn(): string
+ return 'c2-a'
+ enddef
+ endclass
+
+ class C3 extends C2 implements C
+ def C_Fn(): string
+ return 'c3-c'
+ enddef
+ def A_Fn(): string
+ return 'c3-a'
+ enddef
+ def B_Fn(): string
+ return 'c3-b'
+ enddef
+ endclass
+
+ def T1(a: A, s: string)
+ assert_equal(s, a.A_Fn())
+ enddef
+
+ def T2(b: B, s: string)
+ assert_equal(s, b.B_Fn())
+ enddef
+
+ def T3(c: C, s: string)
+ assert_equal(s, c.C_Fn())
+ enddef
+
+ def T4(c1: C1)
+ T1(c1, 'c3-a')
+ enddef
+
+ def T5(c2: C2)
+ T1(c2, 'c3-a')
+ T2(c2, 'c3-b')
+ enddef
+
+ def T6(c3: C3)
+ T1(c3, 'c3-a')
+ T2(c3, 'c3-b')
+ T3(c3, 'c3-c')
+ enddef
+
+ var o3 = C3.new()
+ T4(o3)
+ T5(o3)
+ T6(o3)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Both the parent and child classes implement the same interface
+ lines =<< trim END
+ vim9script
+
+ interface I
+ def Foo(): string
+ endinterface
+
+ class A implements I
+ def Foo(): string
+ return 'A-foo'
+ enddef
+ endclass
+
+ class B implements I
+ def Foo(): string
+ return 'B-foo'
+ enddef
+ endclass
+
+ def Bar(i1: I): string
+ return i1.Foo()
+ enddef
+
+ var b = B.new()
+ assert_equal('B-foo', Bar(b))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
" Test for abstract methods
def Test_abstract_method()
@@ -7282,6 +7389,44 @@
v9.CheckSourceSuccess(lines)
enddef
+" Test for inheriting interfaces from an imported super class
+def Test_interface_inheritance_with_imported_super()
+ var lines =<< trim END
+ vim9script
+
+ export interface I
+ def F(): string
+ endinterface
+
+ export class A implements I
+ def F(): string
+ return 'A'
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xinheritintfimportclass.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+
+ import './Xinheritintfimportclass.vim' as i_imp
+
+ # class C extends i_imp.A
+ class C extends i_imp.A implements i_imp.I
+ def F(): string
+ return 'C'
+ enddef
+ endclass
+
+ def TestI(i: i_imp.I): string
+ return i.F()
+ enddef
+
+ assert_equal('C', TestI(C.new()))
+ 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()
diff --git a/src/version.c b/src/version.c
index a008d32..9dddabc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1012,
+/**/
1011,
/**/
1010,
diff --git a/src/vim9class.c b/src/vim9class.c
index 7c7700b..c3ccf02 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -782,7 +782,7 @@
static int
validate_implements_classes(
garray_T *impl_gap,
- class_T **intf_classes,
+ garray_T *intf_classes_gap,
garray_T *objmethods_gap,
garray_T *objmembers_gap,
class_T *extends_cl)
@@ -812,7 +812,15 @@
}
class_T *ifcl = tv.vval.v_class;
- intf_classes[i] = ifcl;
+ if (ga_grow(intf_classes_gap, 1) == FAIL)
+ {
+ success = FALSE;
+ clear_tv(&tv);
+ break;
+ }
+ ((class_T **)intf_classes_gap->ga_data)[intf_classes_gap->ga_len]
+ = ifcl;
+ intf_classes_gap->ga_len++;
++ifcl->class_refcount;
// check the variables of the interface match the members of the class
@@ -831,6 +839,80 @@
}
/*
+ * Returns TRUE if the interface class "ifcl" is already present in the
+ * "intf_classes_gap" grow array.
+ */
+ static int
+is_interface_class_present(garray_T *intf_classes_gap, class_T *ifcl)
+{
+ for (int j = 0; j < intf_classes_gap->ga_len; j++)
+ {
+ if (((class_T **)intf_classes_gap)[j] == ifcl)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Add interface "ifcl" from a super class to "intf_classes_gap" and the class
+ * name to "impl_gap".
+ */
+ static int
+add_interface_from_super_class(
+ class_T *ifcl,
+ garray_T *impl_gap,
+ garray_T *intf_classes_gap)
+{
+ char_u *intf_name;
+
+ // Add the interface name to "impl_gap"
+ intf_name = vim_strsave(ifcl->class_name);
+ if (intf_name == NULL)
+ return FALSE;
+
+ if (ga_grow(impl_gap, 1) == FAIL)
+ return FALSE;
+
+ char_u **intf_names = (char_u **)impl_gap->ga_data;
+ intf_names[impl_gap->ga_len] = intf_name;
+ impl_gap->ga_len++;
+
+ // Add the interface class to "intf_classes_gap"
+ if (ga_grow(intf_classes_gap, 1) == FAIL)
+ return FALSE;
+
+ class_T **intf_classes = (class_T **)intf_classes_gap->ga_data;
+ intf_classes[intf_classes_gap->ga_len] = ifcl;
+ intf_classes_gap->ga_len++;
+ ++ifcl->class_refcount;
+
+ return TRUE;
+}
+
+/*
+ * Add "super" class interfaces to "intf_classes_gap" (if not present already)
+ * Add the interface class names to "impl_gap".
+ */
+ static int
+add_super_class_interfaces(
+ class_T *super,
+ garray_T *impl_gap,
+ garray_T *intf_classes_gap)
+{
+ // Iterate through all the interfaces implemented by "super"
+ for (int i = 0; i < super->class_interface_count; i++)
+ {
+ class_T *ifcl = super->class_interfaces_cl[i];
+
+ if (!is_interface_class_present(intf_classes_gap, ifcl))
+ add_interface_from_super_class(ifcl, impl_gap, intf_classes_gap);
+ }
+
+ return TRUE;
+}
+
+/*
* Check no function argument name is used as a class member.
* (Object members are always accessed with "this." prefix, so no need
* to check them.)
@@ -2427,14 +2509,23 @@
success = validate_abstract_class_methods(&classfunctions,
&objmethods, extends_cl);
+ // Process the "implements" entries
// Check all "implements" entries are valid.
- if (success && ga_impl.ga_len > 0)
- {
- intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
+ garray_T intf_classes_ga;
- success = validate_implements_classes(&ga_impl, intf_classes,
+ ga_init2(&intf_classes_ga, sizeof(class_T *), 5);
+
+ if (success && ga_impl.ga_len > 0)
+ success = validate_implements_classes(&ga_impl, &intf_classes_ga,
&objmethods, &objmembers, extends_cl);
- }
+
+ // inherit the super class interfaces
+ if (success && extends_cl != NULL)
+ success = add_super_class_interfaces(extends_cl, &ga_impl,
+ &intf_classes_ga);
+
+ intf_classes = intf_classes_ga.ga_data;
+ intf_classes_ga.ga_len = 0;
// Check no function argument name is used as a class member.
if (success)