2008-07-25 Rodrigo Kumpera <rkumpera@novell.com>
authorRodrigo Kumpera <kumpera@gmail.com>
Fri, 25 Jul 2008 17:35:26 +0000 (17:35 -0000)
committerRodrigo Kumpera <kumpera@gmail.com>
Fri, 25 Jul 2008 17:35:26 +0000 (17:35 -0000)
* verify.c: Implement constraint equivalence checking.
This is required when a generic parameter is used as
argument to a constrained one.

Fixes #410637.

svn path=/trunk/mono/; revision=108796

mono/metadata/ChangeLog
mono/metadata/verify.c

index a3b29b24160bf6b243819d4194d241cb1d7e7b98..9283713db18d4e35e1f96dfc214a011a751cb08b 100644 (file)
@@ -1,3 +1,11 @@
+2008-07-25 Rodrigo Kumpera  <rkumpera@novell.com>
+
+       * verify.c: Implement constraint equivalence checking.
+       This is required when a generic parameter is used as
+       argument to a constrained one.
+
+       Fixes #410637.
+
 2008-07-24  Zoltan Varga  <vargaz@gmail.com>
 
        * appdomain.c (MONO_CORLIB_VERSION): Bump corlib version.
index d1cf0a5743de7d37b9e77ef5c19fe8df35d0a0fb..014a7f6a6e04f7946bbbdb9b9a294dd3cdffc8a1 100644 (file)
@@ -477,6 +477,64 @@ is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *co
        return TRUE;
 }
 
+/*
+ * Return true if @candidate is constraint compatible with @target.
+ * 
+ * This means that @candidate constraints are a super set of @target constaints
+ */
+static gboolean
+mono_generic_param_is_constraint_compatible (MonoGenericParam *target, MonoGenericParam *candidate)
+{
+       int tmask = target->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
+       int cmask = candidate->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;    
+       if ((tmask & cmask) != tmask)
+               return FALSE;
+
+       if (target->constraints) {
+               MonoClass **target_class, **candidate_class;
+               if (!candidate->constraints)
+                       return FALSE;
+               for (target_class = target->constraints; *target_class; ++target_class) {
+                       MonoClass *tc = *target_class;
+                       for (candidate_class = candidate->constraints; *candidate_class; ++candidate_class) {
+                               MonoClass *cc = *candidate_class;
+                               if (mono_class_is_assignable_from (tc, cc))
+                                       break;
+                       }
+                       if (!*candidate_class)
+                               return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+static MonoGenericParam*
+verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type)
+{
+       MonoGenericContainer *gc;
+       MonoMethod *method = ctx->method;
+       int num;
+
+       num = type->data.generic_param->num;
+
+       if (type->type == MONO_TYPE_VAR) {
+               MonoClass *gtd = method->klass;
+               if (gtd->generic_class)
+                       gtd = gtd->generic_class->container_class;
+               gc = gtd->generic_container;
+       } else { //MVAR
+               MonoMethod *gmd = method;
+               if (method->is_inflated)
+                       gmd = ((MonoMethodInflated*)method)->declaring;
+               gc = mono_method_get_generic_container (gmd);
+       }
+       if (!gc)
+               return FALSE;
+       return &gc->type_params [num];
+}
+
+
+
 /*
  * Verify if @type is valid for the given @ctx verification context.
  * this function checks for VAR and MVAR types that are invalid under the current verifier,
@@ -513,6 +571,47 @@ is_valid_generic_instantiation_in_context (VerifyContext *ctx, MonoGenericInst *
        return TRUE;
 }
 
+static gboolean
+generic_arguments_respect_constraints (VerifyContext *ctx, MonoGenericContainer *gc, MonoGenericContext *context, MonoGenericInst *ginst)
+{
+       int i;
+       for (i = 0; i < ginst->type_argc; ++i) {
+               MonoType *type = ginst->type_argv [i];
+               MonoGenericParam *target = &gc->type_params [i];
+               MonoGenericParam *candidate;
+
+               if (!mono_type_is_generic_argument (type))
+                       continue;
+
+               if (!is_valid_type_in_context (ctx, type))
+                       return FALSE;
+
+               candidate = verifier_get_generic_param_from_type (ctx, type);
+
+               if (!mono_generic_param_is_constraint_compatible (target, candidate))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+static gboolean
+mono_method_repect_method_constraints (VerifyContext *ctx, MonoMethod *method)
+{
+       MonoMethodInflated *gmethod = (MonoMethodInflated *)method;
+       MonoGenericInst *ginst = gmethod->context.method_inst;
+       MonoGenericContainer *gc = mono_method_get_generic_container (gmethod->declaring);
+       return !gc || generic_arguments_respect_constraints (ctx, gc, &gmethod->context, ginst);
+}
+
+static gboolean
+mono_class_repect_method_constraints (VerifyContext *ctx, MonoClass *klass)
+{
+       MonoGenericClass *gklass = klass->generic_class;
+       MonoGenericInst *ginst = gklass->context.class_inst;
+       MonoGenericContainer *gc = gklass->container_class->generic_container;
+       return !gc || generic_arguments_respect_constraints (ctx, gc, &gklass->context, ginst);
+}
+
 static gboolean
 mono_method_is_valid_generic_instantiation (VerifyContext *ctx, MonoMethod *method)
 {
@@ -561,12 +660,12 @@ mono_type_is_valid_in_context (VerifyContext *ctx, MonoType *type)
                return TRUE;
 
        if (!mono_class_is_valid_generic_instantiation (ctx, klass)) {
-               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
+               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
                return FALSE;
        }
 
-       if (!is_valid_generic_instantiation_in_context (ctx, mono_class_get_context (klass)->class_inst)) {
-               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
+       if (!mono_class_repect_method_constraints (ctx, klass)) {
+               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s (generic args don't respect target's constraints) at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
                return FALSE;
        }
 
@@ -575,8 +674,6 @@ mono_type_is_valid_in_context (VerifyContext *ctx, MonoType *type)
 static gboolean
 mono_method_is_valid_in_context (VerifyContext *ctx, MonoMethod *method)
 {
-       MonoGenericInst *ginst;
-
        if (!mono_type_is_valid_in_context (ctx, &method->klass->byval_arg)) {
                ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
                return FALSE;
@@ -586,13 +683,12 @@ mono_method_is_valid_in_context (VerifyContext *ctx, MonoMethod *method)
                return TRUE;
 
        if (!mono_method_is_valid_generic_instantiation (ctx, method)) {
-               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_UNVERIFIABLE_IL);
+               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_UNVERIFIABLE_IL);
                return FALSE;
        }
 
-       ginst = mono_method_get_context (method)->method_inst;
-       if (ginst && !is_valid_generic_instantiation_in_context (ctx, ginst)) {
-               ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
+       if (!mono_method_repect_method_constraints (ctx, method)) {
+               CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s (generic args don't respect target's constraints) at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset));
                return FALSE;
        }
        return TRUE;