From: Yegappan Lakshmanan Date: Mon, 13 Jan 2025 06:30:11 +0000 (+0100) Subject: patch 9.1.1012: Vim9: class interface inheritance not correctly working X-Git-Tag: v9.1.1012^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e92db4ea22f010d402df9d34071022a4aa8cef5;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index c20de25337..23281bcad7 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -6096,44 +6096,151 @@ enddef " 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 @@ def Test_implement_interface_with_different_variable_order() 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 a008d32404..9dddabc05a 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1012, /**/ 1011, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 7c7700ba19..c3ccf0250a 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -782,7 +782,7 @@ validate_interface_methods( 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 @@ validate_implements_classes( } 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 @@ -830,6 +838,80 @@ validate_implements_classes( return success; } +/* + * 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 @@ -2427,14 +2509,23 @@ early_ret: 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)