[class] Use constraints of gparam T2 in T1.IsAssignableFrom (T2)
authorAleksey Kliger <aleksey@xamarin.com>
Fri, 22 Sep 2017 14:30:44 +0000 (10:30 -0400)
committerAleksey Kliger (λgeek) <akliger@gmail.com>
Tue, 26 Sep 2017 19:59:46 +0000 (15:59 -0400)
Suppose we want to know C.IsAssignableFrom (T1):
```
  class C { }
  class G<T1, T2> where T1 : T2, T2 : C { }
```

Previously, we would get `False`, because we only checked the constraints of T1
if C was an interface or another gparam.  Now Instead we always consider the
constraints in `A.IsAssignableFrom (B)` whenever `B` is a gparam.

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=58809

mono/metadata/class.c

index f3e87fe6428f9296d921a09a1405f21ae87f235f..e1539de106cd4bb92eaa32c27f30e5795facc6cd 100644 (file)
@@ -8226,22 +8226,31 @@ mono_class_is_assignable_from (MonoClass *klass, MonoClass *oklass)
                return mono_gparam_is_assignable_from (klass, oklass);
        }
 
-       if (MONO_CLASS_IS_INTERFACE (klass)) {
-               if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) {
-                       MonoGenericParam *gparam = oklass->byval_arg.data.generic_param;
-                       MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints;
-                       int i;
+       /* This can happen if oklass is a tyvar that has a constraint which is another tyvar which in turn
+        * has a constraint which is a class type:
+        *
+        *  class Foo { }
+        *  class G<T1, T2> where T1 : T2 where T2 : Foo { }
+        *
+        * In this case, Foo is assignable from T1.
+        */
+       if ((oklass->byval_arg.type == MONO_TYPE_VAR) || (oklass->byval_arg.type == MONO_TYPE_MVAR)) {
+               MonoGenericParam *gparam = oklass->byval_arg.data.generic_param;
+               MonoClass **constraints = mono_generic_container_get_param_info (gparam->owner, gparam->num)->constraints;
+               int i;
 
-                       if (constraints) {
-                               for (i = 0; constraints [i]; ++i) {
-                                       if (mono_class_is_assignable_from (klass, constraints [i]))
-                                               return TRUE;
-                               }
+               if (constraints) {
+                       for (i = 0; constraints [i]; ++i) {
+                               if (mono_class_is_assignable_from (klass, constraints [i]))
+                                       return TRUE;
                        }
-
-                       return FALSE;
                }
 
+               return mono_class_has_parent (oklass, klass);
+       }
+
+       if (MONO_CLASS_IS_INTERFACE (klass)) {
+
                /* interface_offsets might not be set for dynamic classes */
                if (mono_class_get_ref_info_handle (oklass) && !oklass->interface_bitmap) {
                        /*