From a94bd9d9396183eb7781f8d6a5a0e6e97442e9ed Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 12 Jan 2023 15:01:32 +0000 Subject: [PATCH] patch 9.0.1184: interface of an object is not recognized when checking type Problem: Interface of an object is not recognized when checking type. Solution: Use the interface implemented by an object. --- src/structs.h | 1 + src/testdir/test_vim9_class.vim | 18 +++++++++++++++++ src/version.c | 2 ++ src/vim9class.c | 36 ++++++++++++++++++++++++++++++++- src/vim9type.c | 10 +++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/structs.h b/src/structs.h index 89ed23dbd..80dc4730e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1500,6 +1500,7 @@ struct class_S // interfaces declared for the class int class_interface_count; char_u **class_interfaces; // allocated array of names + class_T **class_interfaces_cl; // interfaces (counts as reference) // class members: "static varname" int class_class_member_count; diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 139a632ed..74df0f439 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -455,6 +455,24 @@ def Test_object_type() var o: One = Two.new() END v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got object') + + lines =<< trim END + vim9script + + interface One + def GetMember(): number + endinterface + class Two implements One + this.one = 1 + def GetMember(): number + return this.one + enddef + endclass + + var o: One = Two.new(5) + assert_equal(5, o.GetMember()) + END + v9.CheckScriptSuccess(lines) enddef def Test_class_member() diff --git a/src/version.c b/src/version.c index be6600a37..e0d5bffa0 100644 --- a/src/version.c +++ b/src/version.c @@ -695,6 +695,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1184, /**/ 1183, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 0970619b9..a60e70d2a 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -582,9 +582,13 @@ early_ret: } VIM_CLEAR(extends); + class_T **intf_classes = NULL; + // Check all "implements" entries are valid. if (success && ga_impl.ga_len > 0) { + intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len); + for (int i = 0; i < ga_impl.ga_len && success; ++i) { char_u *impl = ((char_u **)ga_impl.ga_data)[i]; @@ -605,8 +609,11 @@ early_ret: success = FALSE; } - // check the members of the interface match the members of the class class_T *ifcl = tv.vval.v_class; + intf_classes[i] = ifcl; + ++ifcl->class_refcount; + + // check the members of the interface match the members of the class for (int loop = 1; loop <= 2 && success; ++loop) { // loop == 1: check class members @@ -717,6 +724,9 @@ early_ret: cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i]; VIM_CLEAR(ga_impl.ga_data); ga_impl.ga_len = 0; + + cl->class_interfaces_cl = intf_classes; + intf_classes = NULL; } // Add class and object members to "cl". @@ -930,6 +940,18 @@ cleanup: { vim_free(cl->class_name); vim_free(cl->class_class_functions); + if (cl->class_interfaces != NULL) + { + for (int i = 0; i < cl->class_interface_count; ++i) + vim_free(cl->class_interfaces[i]); + vim_free(cl->class_interfaces); + } + if (cl->class_interfaces_cl != NULL) + { + for (int i = 0; i < cl->class_interface_count; ++i) + class_unref(cl->class_interfaces_cl[i]); + vim_free(cl->class_interfaces_cl); + } vim_free(cl->class_obj_members); vim_free(cl->class_obj_methods); vim_free(cl); @@ -937,6 +959,13 @@ cleanup: vim_free(extends); class_unref(extends_cl); + + if (intf_classes != NULL) + { + for (int i = 0; i < ga_impl.ga_len; ++i) + class_unref(intf_classes[i]); + vim_free(intf_classes); + } ga_clear_strings(&ga_impl); for (int round = 1; round <= 2; ++round) @@ -1321,8 +1350,13 @@ class_unref(class_T *cl) class_unref(cl->class_extends); for (int i = 0; i < cl->class_interface_count; ++i) + { vim_free(((char_u **)cl->class_interfaces)[i]); + if (cl->class_interfaces_cl[i] != NULL) + class_unref(cl->class_interfaces_cl[i]); + } vim_free(cl->class_interfaces); + vim_free(cl->class_interfaces_cl); for (int i = 0; i < cl->class_class_member_count; ++i) { diff --git a/src/vim9type.c b/src/vim9type.c index 5d3f815b5..02b674d23 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -876,11 +876,21 @@ check_type_maybe( } else if (expected->tt_type == VAR_OBJECT) { + // check the class, base class or an implemented interface matches class_T *cl; for (cl = (class_T *)actual->tt_member; cl != NULL; cl = cl->class_extends) + { if ((class_T *)expected->tt_member == cl) break; + int i; + for (i = cl->class_interface_count - 1; i >= 0; --i) + if ((class_T *)expected->tt_member + == cl->class_interfaces_cl[i]) + break; + if (i >= 0) + break; + } if (cl == NULL) ret = FAIL; } -- 2.40.0