+static gboolean
+mono_gparam_is_assignable_from (MonoClass *target, MonoClass *candidate)
+{
+ MonoGenericParam *gparam, *ogparam;
+ MonoGenericParamInfo *tinfo, *cinfo;
+ MonoClass **candidate_class;
+ gboolean class_constraint_satisfied, valuetype_constraint_satisfied;
+ int tmask, cmask;
+
+ if (target == candidate)
+ return TRUE;
+ if (target->byval_arg.type != candidate->byval_arg.type)
+ return FALSE;
+
+ gparam = target->byval_arg.data.generic_param;
+ ogparam = candidate->byval_arg.data.generic_param;
+ tinfo = mono_generic_param_info (gparam);
+ cinfo = mono_generic_param_info (ogparam);
+
+ class_constraint_satisfied = FALSE;
+ valuetype_constraint_satisfied = FALSE;
+
+ /*candidate must have a super set of target's special constraints*/
+ tmask = tinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
+ cmask = cinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
+
+ if (cinfo->constraints) {
+ for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) {
+ MonoClass *cc = *candidate_class;
+
+ if (mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc))
+ class_constraint_satisfied = TRUE;
+ else if (!mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc))
+ valuetype_constraint_satisfied = TRUE;
+ }
+ }
+ class_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0;
+ valuetype_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0;
+
+ if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && !class_constraint_satisfied)
+ return FALSE;
+ if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && !valuetype_constraint_satisfied)
+ return FALSE;
+ if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !((cmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) ||
+ valuetype_constraint_satisfied)) {
+ return FALSE;
+ }
+
+
+ /*candidate type constraints must be a superset of target's*/
+ if (tinfo->constraints) {
+ MonoClass **target_class;
+ for (target_class = tinfo->constraints; *target_class; ++target_class) {
+ MonoClass *tc = *target_class;
+
+ /*
+ * A constraint from @target might inflate into @candidate itself and in that case we don't need
+ * check it's constraints since it satisfy the constraint by itself.
+ */
+ if (mono_metadata_type_equal (&tc->byval_arg, &candidate->byval_arg))
+ continue;
+
+ if (!cinfo->constraints)
+ return FALSE;
+
+ for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) {
+ MonoClass *cc = *candidate_class;
+
+ if (mono_class_is_assignable_from (tc, cc))
+ break;
+
+ /*
+ * This happens when we have the following:
+ *
+ * Bar<K> where K : IFace
+ * Foo<T, U> where T : U where U : IFace
+ * ...
+ * Bar<T> <- T here satisfy K constraint transitively through to U's constraint
+ *
+ */
+ if (mono_type_is_generic_argument (&cc->byval_arg)) {
+ if (mono_gparam_is_assignable_from (target, cc))
+ break;
+ }
+ }
+ if (!*candidate_class)
+ return FALSE;
+ }
+ }
+
+ /*candidate itself must have a constraint that satisfy target*/
+ if (cinfo->constraints) {
+ for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) {
+ MonoClass *cc = *candidate_class;
+ if (mono_class_is_assignable_from (target, cc))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+