# HG changeset patch # User Christian Brabandt # Date 1696276803 -7200 # Node ID 97ceabebaeaf5ca86e3d83edd881728481b6a18e # Parent 94a2ae015d9d16aa6b12bbf74cd81c156dcd6fc6 patch 9.0.1974: vim9: using contra-variant type-checks Commit: https://github.com/vim/vim/commit/b32064fedbeb689ecb0481e9473cb7b87d5bb805 Author: Yegappan Lakshmanan Date: Mon Oct 2 21:43:58 2023 +0200 patch 9.0.1974: vim9: using contra-variant type-checks Problem: vim9: using contra-variant type-checks (after v9.0.1959) Solution: Use invariant type checking instead closes: #13248 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt --- a/runtime/doc/vim9class.txt +++ b/runtime/doc/vim9class.txt @@ -547,11 +547,9 @@ Object variables from the base class are is not possible to override them (unlike some other languages). *E1356* *E1357* *E1358* -Object methods of the base class can be overruled. The number of arguments -must be exactly the same. The method argument type can be a contra-variant -type of the base class method argument type. The method return value type can -be a covariant type of the base class method return value type. The method of -the base class can be called by prefixing "super.". +Object methods of the base class can be overruled. The signature (arguments, +argument types and return type) must be exactly the same. The method of the +base class can be called by prefixing "super.". *E1377* The access level of a method (public or private) in a child class should be diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -29,5 +29,5 @@ int object_free_nonref(int copyID); void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void f_instanceof(typval_T *argvars, typval_T *rettv); -int class_instance_of(class_T *cl, class_T *other_cl, int covariance_check); +int class_instance_of(class_T *cl, class_T *other_cl); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -6338,7 +6338,31 @@ def Test_extended_obj_method_type_check( endclass class Bar extends Foo - def Doit(p: A): C + def Doit(p: C): B + return B.new() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + class Foo + def Doit(p: B): B + return B.new() + enddef + endclass + + class Bar extends Foo + def Doit(p: B): C return C.new() enddef endclass @@ -6362,12 +6386,12 @@ def Test_extended_obj_method_type_check( endclass class Bar extends Foo - def Doit(p: C): B + def Doit(p: A): B return B.new() enddef endclass END - v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) + v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1974, +/**/ 1973, /**/ 1972, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -2561,7 +2561,7 @@ inside_class(cctx_T *cctx_arg, class_T * { for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer) if (cctx->ctx_ufunc != NULL - && class_instance_of(cctx->ctx_ufunc->uf_class, cl, TRUE)) + && class_instance_of(cctx->ctx_ufunc->uf_class, cl)) return TRUE; return FALSE; } @@ -2871,39 +2871,29 @@ member_not_found_msg(class_T *cl, vartyp * interfaces matches the class "other_cl". */ int -class_instance_of(class_T *cl, class_T *other_cl, int covariance_check) +class_instance_of(class_T *cl, class_T *other_cl) { if (cl == other_cl) return TRUE; - if (covariance_check) + // Recursively check the base classes. + for (; cl != NULL; cl = cl->class_extends) { - // Recursively check the base classes. - for (; cl != NULL; cl = cl->class_extends) + if (cl == other_cl) + return TRUE; + // Check the implemented interfaces and the super interfaces + for (int i = cl->class_interface_count - 1; i >= 0; --i) { - if (cl == other_cl) - return TRUE; - // Check the implemented interfaces and the super interfaces - for (int i = cl->class_interface_count - 1; i >= 0; --i) + class_T *intf = cl->class_interfaces_cl[i]; + while (intf != NULL) { - 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; - } + if (intf == other_cl) + return TRUE; + // check the super interfaces + intf = intf->class_extends; } } } - else - { - // contra-variance - for (; other_cl != NULL; other_cl = other_cl->class_extends) - if (cl == other_cl) - return TRUE; - } return FALSE; } @@ -2938,7 +2928,7 @@ f_instanceof(typval_T *argvars, typval_T } if (class_instance_of(object_tv->vval.v_object->obj_class, - li->li_tv.vval.v_class, TRUE) == TRUE) + li->li_tv.vval.v_class) == TRUE) { rettv->vval.v_number = VVAL_TRUE; return; @@ -2947,9 +2937,8 @@ f_instanceof(typval_T *argvars, typval_T } else if (classinfo_tv->v_type == VAR_CLASS) { - rettv->vval.v_number = class_instance_of( - object_tv->vval.v_object->obj_class, - classinfo_tv->vval.v_class, TRUE); + rettv->vval.v_number = class_instance_of(object_tv->vval.v_object->obj_class, + classinfo_tv->vval.v_class); } } diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -925,10 +925,14 @@ check_type_maybe( if (actual->tt_class == NULL) return OK; // A null object matches - // For object method arguments, do a contra-variance type check in - // an extended class. For all others, do a co-variance type check. - if (class_instance_of(actual->tt_class, expected->tt_class, - where.wt_kind != WT_METHOD_ARG) == FALSE) + // For object method arguments, do a invariant type check in + // an extended class. For all others, do a covariance type check. + if (where.wt_kind == WT_METHOD_ARG) + { + if (actual->tt_class != expected->tt_class) + ret = FAIL; + } + else if (!class_instance_of(actual->tt_class, expected->tt_class)) ret = FAIL; }