// Compare two sets of generic type parameters to see if they're assignment compatible taking generic // variance into account. It's assumed they've already had their type definition matched (which // implies their arities are the same as well). The fForceCovariance argument tells the method to // override the defined variance of each parameter and instead assume it is covariant. This is used to // implement covariant array interfaces. static internal unsafe bool TypeParametersAreCompatible(int arity, EETypeRef* pSourceInstantiation, EETypeRef* pTargetInstantiation, GenericVariance* pVarianceInfo, bool fForceCovariance) { // Walk through the instantiations comparing the cast compatibility of each pair // of type args. for (int i = 0; i < arity; i++) { EEType* pTargetArgType = pTargetInstantiation[i].Value; EEType* pSourceArgType = pSourceInstantiation[i].Value; GenericVariance varType; if (fForceCovariance) varType = GenericVariance.ArrayCovariant; else varType = pVarianceInfo[i]; switch (varType) { case GenericVariance.NonVariant: // Non-variant type params need to be identical. if (!AreTypesEquivalentInternal(pSourceArgType, pTargetArgType)) return false; break; case GenericVariance.Covariant: // For covariance (or out type params in C#) the object must implement an // interface with a more derived type arg than the target interface. Or // the object interface can have a type arg that is an interface // implemented by the target type arg. // For instance: // class Foo : ICovariant<String> is ICovariant<Object> // class Foo : ICovariant<Bar> is ICovariant<IBar> // class Foo : ICovariant<IBar> is ICovariant<Object> if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.Normal)) return false; break; case GenericVariance.ArrayCovariant: // For array covariance the object must be an array with a type arg // that is more derived than that the target interface, or be a primitive // (or enum) with the same size. // For instance: // string[,,] is object[,,] // int[,,] is uint[,,] // This call is just like the call for Covariance above except true is passed // to the fAllowSizeEquivalence parameter to allow the int/uint matching to work if (!CastCache.AreTypesAssignableInternal(pSourceArgType, pTargetArgType, AssignmentVariation.AllowSizeEquivalence)) return false; break; case GenericVariance.Contravariant: // For contravariance (or in type params in C#) the object must implement // an interface with a less derived type arg than the target interface. Or // the object interface can have a type arg that is a class implementing // the interface that is the target type arg. // For instance: // class Foo : IContravariant<Object> is IContravariant<String> // class Foo : IContravariant<IBar> is IContravariant<Bar> // class Foo : IContravariant<Object> is IContravariant<IBar> if (!CastCache.AreTypesAssignableInternal(pTargetArgType, pSourceArgType, AssignmentVariation.Normal)) return false; break; default: Debug.Assert(false, "unknown generic variance type"); break; } } return true; }
internal extern static unsafe EEType* RhGetGenericInstantiation(EEType* pEEType, int* pArity, EETypeRef** ppInstantiation, GenericVariance** ppVarianceInfo);