[Mono-dev] Generic Variance

Scott Peterson scott at ssblack.co.nz
Sun Feb 1 09:46:28 EST 2009


This is a patch to add runtime support for generic variance. This patch
lacks certain features of the spec. For example, only reference type
variance is supported. Also, the runtime does not enforce the validity of
variant parameters (that they only appear in interface and delegate types,
that covariant parameters only appear as return types and in interface
inheritance, and the contravariant parameters only appear as by-value method
parameter types). This means it is possible to violate type safety will
invalid CIL. The patch is contributed under the MIT/X11 license. It is
attached and inline.

Index: mono/metadata/class.c
===================================================================
--- mono/metadata/class.c    (revision 125254)
+++ mono/metadata/class.c    (working copy)
@@ -2034,8 +2034,57 @@
     return (key->interface_id - element->interface_id);
 }

+static gboolean
+mono_class_has_variant_generic_params (MonoClass *klass)
+{
+    int i;
+    MonoGenericContainer *container;
+
+    if (!klass->generic_class)
+        return FALSE;
+
+    container = klass->generic_class->container_class->generic_container;
+
+    for (i = 0; i < container->type_argc; ++i)
+        if (container->type_params [i].flags &
GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK)
+            return TRUE;
+
+    return FALSE;
+}
+
+static gboolean
+mono_class_is_variant_of (MonoClass *klass, MonoClass *vklass) {
+    int i;
+    MonoClass *generic = klass->generic_class->container_class;
+    MonoClass *vgeneric = vklass->generic_class->container_class;
+    MonoGenericContainer *container = vgeneric->generic_container;
+
+    if (generic != vgeneric)
+        return FALSE;
+
+    for (i = 0; i < container->type_argc; i++) {
+        MonoClass *param_class = mono_class_from_mono_type
(klass->generic_class->context.class_inst->type_argv [i]);
+        MonoClass *vparam_class = mono_class_from_mono_type
(vklass->generic_class->context.class_inst->type_argv [i]);
+
+        // FIXME this is incorrect
+        if (param_class->valuetype || vparam_class->valuetype)
+            return FALSE;
+
+        if (container->type_params [i].flags &
GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK) {
+            if ((container->type_params [i].flags &
GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT) && !mono_class_is_assignable_from
(param_class, vparam_class))
+                return FALSE;
+            if ((container->type_params [i].flags &
GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) && !mono_class_is_assignable_from
(vparam_class, param_class))
+                return FALSE;
+        } else if (param_class != vparam_class)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
 int
 mono_class_interface_offset (MonoClass *klass, MonoClass *itf) {
+    int i;
     MonoClass **result = bsearch (
             itf,
             klass->interfaces_packed,
@@ -2044,9 +2093,14 @@
             compare_interface_ids);
     if (result) {
         return klass->interface_offsets_packed [result -
(klass->interfaces_packed)];
-    } else {
-        return -1;
+    } else if (mono_class_has_variant_generic_params (itf)) {
+        for (i = 0; i < klass->interface_offsets_count; i++) {
+            if (mono_class_is_variant_of (klass->interfaces_packed[i],
itf)) {
+                return klass->interface_offsets_packed [i];
+            }
+        }
     }
+    return -1;
 }

 static void
@@ -5797,24 +5851,6 @@
     return FALSE;
 }

-static gboolean
-mono_class_has_variant_generic_params (MonoClass *klass)
-{
-    int i;
-    MonoGenericContainer *container;
-
-    if (!klass->generic_class)
-        return FALSE;
-
-    container = klass->generic_class->container_class->generic_container;
-
-    for (i = 0; i < container->type_argc; ++i)
-        if (container->type_params [i].flags &
(MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT))
-            return TRUE;
-
-    return FALSE;
-}
-
 /**
  * mono_class_is_assignable_from:
  * @klass: the class to be assigned to
@@ -5852,55 +5888,23 @@
         if (MONO_CLASS_IMPLEMENTS_INTERFACE (oklass, klass->interface_id))
             return TRUE;

-        if (mono_class_has_variant_generic_params (klass)) {
-            if (oklass->generic_class) {
-                int i;
-                gboolean match = FALSE;
-                MonoClass *container_class1 =
klass->generic_class->container_class;
-                MonoClass *container_class2 =
oklass->generic_class->container_class;
+        if (mono_class_has_variant_generic_params (klass) &&
oklass->generic_class) {
+            int i;
+            gboolean match = FALSE;
+            MonoClass *container_class1 =
klass->generic_class->container_class;
+            MonoClass *container_class2 =
oklass->generic_class->container_class;

-                /*
-                 * Check whenever the generic definition of oklass
implements the
-                 * generic definition of klass. The IMPLEMENTS_INTERFACE
stuff is not usable
-                 * here since the relevant tables are not set up.
-                 */
-                for (i = 0; i < container_class2->interface_offsets_count;
++i)
-                    if ((container_class2->interfaces_packed [i] ==
container_class1) || (container_class2->interfaces_packed [i]->generic_class
&& (container_class2->interfaces_packed [i]->generic_class->container_class
== container_class1)))
-                        match = TRUE;
-
-                if (match) {
-                    MonoGenericContainer *container;
-
-                    container =
klass->generic_class->container_class->generic_container;
-
+            /*
+             * Check whenever the generic definition of oklass implements
the
+             * generic definition of klass. The IMPLEMENTS_INTERFACE stuff
is not usable
+             * here since the relevant tables are not set up.
+             */
+            for (i = 0; i < container_class2->interface_offsets_count; ++i)
+                if ((container_class2->interfaces_packed [i] ==
container_class1) || (container_class2->interfaces_packed [i]->generic_class
&& (container_class2->interfaces_packed [i]->generic_class->container_class
== container_class1)))
                     match = TRUE;
-                    for (i = 0; i < container->type_argc; ++i) {
-                        MonoClass *param1_class = mono_class_from_mono_type
(klass->generic_class->context.class_inst->type_argv [i]);
-                        MonoClass *param2_class = mono_class_from_mono_type
(oklass->generic_class->context.class_inst->type_argv [i]);

-                        if (param1_class->valuetype !=
param2_class->valuetype) {
-                            match = FALSE;
-                            break;
-                        }
-                        /*
-                         * The _VARIANT and _COVARIANT constants should
read _COVARIANT and
-                         * _CONTRAVARIANT, but they are in a public header
so we can't fix it.
-                         */
-                        if (param1_class != param2_class) {
-                            if ((container->type_params [i].flags &
MONO_GEN_PARAM_VARIANT) && mono_class_is_assignable_from (param1_class,
param2_class))
-                                ;
-                            else if (((container->type_params [i].flags &
MONO_GEN_PARAM_COVARIANT) && mono_class_is_assignable_from (param2_class,
param1_class)))
-                                ;
-                            else {
-                                match = FALSE;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (match)
-                        return TRUE;
-                }
+            if (match && mono_class_is_variant_of (oklass, klass)) {
+                return TRUE;
             }
         }
     } else if (klass->rank) {
Index: mono/metadata/ChangeLog
===================================================================
--- mono/metadata/ChangeLog    (revision 125254)
+++ mono/metadata/ChangeLog    (working copy)
@@ -1,3 +1,14 @@
+2009-02-01  Scott Peterson  <lunchtimemama at gmail.com>
+
+    This adds runtime generic variance support for reference types.
+    This patch is contributed under the MIT/X11 license.
+
+    * class.c: Added mono_class_has_variant_generic_params which determins
+    if a generic class has a variant type parameter. Added
+    mono_class_is_variant_of which determins if the first class is a legal
+    variant of the second. Modified mono_class_interface_offset and
+    mono_class_is_assignable_from to use these two new methods.
+
 2009-01-31  Zoltan Varga  <vargaz at gmail.com>

     * mono-debug.c (mono_debug_lookup_locals): New function to return local
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.ximian.com/pipermail/mono-devel-list/attachments/20090202/2be7fb1e/attachment-0001.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: variance.patch
Type: text/x-patch
Size: 6952 bytes
Desc: not available
Url : http://lists.ximian.com/pipermail/mono-devel-list/attachments/20090202/2be7fb1e/attachment-0001.bin 


More information about the Mono-devel-list mailing list