internal GenericArgumentCollection(uint argumentCount, EETypeRef *arguments) { _argumentCount = argumentCount; _arguments = arguments; }
private static bool FindImplSlotInSimpleMap(EEType *pTgtType, EEType *pItfType, uint itfSlotNumber, ushort *pImplSlotNumber, bool actuallyCheckVariance) { Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map"); EEType * pItfOpenGenericType = null; EETypeRef * pItfInstantiation = null; int itfArity = 0; GenericVariance *pItfVarianceInfo = null; bool fCheckVariance = false; bool fArrayCovariance = false; if (actuallyCheckVariance) { fCheckVariance = pItfType->HasGenericVariance; fArrayCovariance = pTgtType->IsArray; // Non-arrays can follow array variance rules iff // 1. They have one generic parameter // 2. That generic parameter is array covariant. // // This special case is to allow array enumerators to work if (!fArrayCovariance && pTgtType->HasGenericVariance) { int tgtEntryArity = (int)pTgtType->GenericArity; GenericVariance *pTgtVarianceInfo = pTgtType->GenericVariance; if ((tgtEntryArity == 1) && pTgtVarianceInfo[0] == GenericVariance.ArrayCovariant) { fArrayCovariance = true; } } // Arrays are covariant even though you can both get and set elements (type safety is maintained by // runtime type checks during set operations). This extends to generic interfaces implemented on those // arrays. We handle this by forcing all generic interfaces on arrays to behave as though they were // covariant (over their one type parameter corresponding to the array element type). if (fArrayCovariance && pItfType->IsGeneric) { fCheckVariance = true; } // If there is no variance checking, there is no operation to perform. (The non-variance check loop // has already completed) if (!fCheckVariance) { return(false); } } DispatchMap *pMap = pTgtType->DispatchMap; DispatchMap.DispatchMapEntry *i = (*pMap)[0]; DispatchMap.DispatchMapEntry *iEnd = (*pMap)[(int)pMap->NumEntries]; for (; i != iEnd; ++i) { if (i->_usInterfaceMethodSlot == itfSlotNumber) { EEType *pCurEntryType = pTgtType->InterfaceMap[i->_usInterfaceIndex].InterfaceType; if (pCurEntryType->IsCloned) { pCurEntryType = pCurEntryType->CanonicalEEType; } if (pCurEntryType == pItfType) { *pImplSlotNumber = i->_usImplMethodSlot; return(true); } else if (fCheckVariance && ((fArrayCovariance && pCurEntryType->IsGeneric) || pCurEntryType->HasGenericVariance)) { // Interface types don't match exactly but both the target interface and the current interface // in the map are marked as being generic with at least one co- or contra- variant type // parameter. So we might still have a compatible match. // Retrieve the unified generic instance for the callsite interface if we haven't already (we // lazily get this then cache the result since the lookup isn't necessarily cheap). if (pItfOpenGenericType == null) { pItfOpenGenericType = pItfType->GenericDefinition; itfArity = (int)pItfType->GenericArity; pItfInstantiation = pItfType->GenericArguments; pItfVarianceInfo = pItfType->GenericVariance; } // Retrieve the unified generic instance for the interface we're looking at in the map. EEType *pCurEntryGenericType = pCurEntryType->GenericDefinition; // If the generic types aren't the same then the types aren't compatible. if (pItfOpenGenericType != pCurEntryGenericType) { continue; } // Grab instantiation details for the candidate interface. EETypeRef *pCurEntryInstantiation = pCurEntryType->GenericArguments; // The types represent different instantiations of the same generic type. The // arity of both had better be the same. Debug.Assert(itfArity == (int)pCurEntryType->GenericArity, "arity mismatch betweeen generic instantiations"); if (TypeCast.TypeParametersAreCompatible(itfArity, pCurEntryInstantiation, pItfInstantiation, pItfVarianceInfo, fArrayCovariance, null)) { *pImplSlotNumber = i->_usImplMethodSlot; return(true); } } } } return(false); }
// 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 (!AreTypesAssignableInternal(pSourceArgType, pTargetArgType, false, false)) { 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 (!AreTypesAssignableInternal(pSourceArgType, pTargetArgType, false, true)) { 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 (!AreTypesAssignableInternal(pTargetArgType, pSourceArgType, false, false)) { return(false); } break; default: Debug.Assert(false, "unknown generic variance type"); break; } } return(true); }