changeset 33432:97ceabebaeaf v9.0.1974

patch 9.0.1974: vim9: using contra-variant type-checks Commit: https://github.com/vim/vim/commit/b32064fedbeb689ecb0481e9473cb7b87d5bb805 Author: Yegappan Lakshmanan <yegappan@yahoo.com> 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 <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Mon, 02 Oct 2023 22:00:03 +0200
parents 94a2ae015d9d
children c66a146cfebc
files runtime/doc/vim9class.txt src/proto/vim9class.pro src/testdir/test_vim9_class.vim src/version.c src/vim9class.c src/vim9type.c
diffstat 6 files changed, 58 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 : */
--- 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<B>): object<B> but got func(object<C>): object<B>', 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<B>): object<B> but got func(object<C>): object<B>', 20)
+  v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<A>): object<B>', 20)
 
   lines =<< trim END
     vim9script
--- 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,
--- 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);
     }
 }
 
--- 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;
 	}