patch 9.0.1959: Vim9: methods parameters and types are covariant

Problem:  Vim9: methods parameters and types are covariant
Solution: Support contra-variant type check for object method arguments
          (similar to Dart).

closes: #12965
closes: #13221

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
diff --git a/src/vim9class.c b/src/vim9class.c
index 885ac03..790c2c3 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2561,7 +2561,7 @@
 {
     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))
+			&& class_instance_of(cctx->ctx_ufunc->uf_class, cl, TRUE))
 	    return TRUE;
     return FALSE;
 }
@@ -2871,29 +2871,39 @@
  * interfaces matches the class "other_cl".
  */
     int
-class_instance_of(class_T *cl, class_T *other_cl)
+class_instance_of(class_T *cl, class_T *other_cl, int covariance_check)
 {
     if (cl == other_cl)
 	return TRUE;
 
-    // Recursively check the base classes.
-    for (; cl != NULL; cl = cl->class_extends)
+    if (covariance_check)
     {
-	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)
+	// Recursively check the base classes.
+	for (; cl != NULL; cl = cl->class_extends)
 	{
-	    class_T	*intf = cl->class_interfaces_cl[i];
-	    while (intf != NULL)
+	    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 (intf == other_cl)
-		    return TRUE;
-		// check the super interfaces
-		intf = intf->class_extends;
+		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;
+		}
 	    }
 	}
     }
+    else
+    {
+	// contra-variance
+	for (; other_cl != NULL; other_cl = other_cl->class_extends)
+	    if (cl == other_cl)
+		return TRUE;
+    }
 
     return FALSE;
 }
@@ -2928,7 +2938,7 @@
 	    }
 
 	    if (class_instance_of(object_tv->vval.v_object->obj_class,
-			li->li_tv.vval.v_class) == TRUE)
+			li->li_tv.vval.v_class, TRUE) == TRUE)
 	    {
 		rettv->vval.v_number = VVAL_TRUE;
 		return;
@@ -2937,8 +2947,9 @@
     }
     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);
+	rettv->vval.v_number = class_instance_of(
+					object_tv->vval.v_object->obj_class,
+					classinfo_tv->vval.v_class, TRUE);
     }
 }