public static bool IsVariantMethodCall(NodeFactory factory, MethodDesc calledMethod) { Debug.Assert(calledMethod.IsVirtual); TypeDesc owningType = calledMethod.OwningType; if (!owningType.IsInterface) { return(false); } bool result = false; if (owningType.HasVariance) { TypeDesc owningTypeDefinition = owningType.GetTypeDefinition(); for (int i = 0; i < owningTypeDefinition.Instantiation.Length; i++) { var variantParameter = (GenericParameterDesc)owningTypeDefinition.Instantiation[i]; if (variantParameter.Variance != 0) { // Variant interface parameters that are instantiated over valuetypes are // not actually variant. Neither are contravariant parameters instantiated // over sealed types (there won't be another interface castable to it // through variance on that parameter). TypeDesc variantArgument = owningType.Instantiation[i]; if (!variantArgument.IsValueType && (!variantArgument.IsSealed() || variantParameter.IsCovariant)) { result = true; break; } } } } if (!result && factory.TypeSystemContext.IsGenericArrayInterfaceType(owningType)) { // We need to also do this for generic interfaces on arrays because they have a weird casting rule // that doesn't require the implemented interface to be variant to consider it castable. // For value types, we only need this when the array is castable by size (int[] and ICollection<uint>), // or it's a reference type (Derived[] and ICollection<Base>). TypeDesc elementType = owningType.Instantiation[0]; result = CastingHelper.IsArrayElementTypeCastableBySize(elementType) || (elementType.IsDefType && !elementType.IsValueType); } return(result); }
public static bool IsVariantInterfaceImplementation(NodeFactory factory, TypeDesc providingType, DefType implementedInterface) { Debug.Assert(implementedInterface.IsInterface); Debug.Assert(!implementedInterface.IsGenericDefinition); // If any of the implemented interfaces have variance, calls against compatible interface methods // could result in interface methods of this type being used (e.g. IEnumerable<object>.GetEnumerator() // can dispatch to an implementation of IEnumerable<string>.GetEnumerator()). bool result = false; if (implementedInterface.HasVariance) { TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) { var variantParameter = (GenericParameterDesc)interfaceDefinition.Instantiation[i]; if (variantParameter.Variance != 0) { // Variant interface parameters that are instantiated over valuetypes are // not actually variant. Neither are contravariant parameters instantiated // over sealed types (there won't be another interface castable to it // through variance on that parameter). TypeDesc variantArgument = implementedInterface.Instantiation[i]; if (!variantArgument.IsValueType && (!variantArgument.IsSealed() || variantParameter.IsCovariant)) { result = true; break; } } } } if (!result && (providingType.IsArray || providingType.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && implementedInterface.HasInstantiation) { // We need to also do this for generic interfaces on arrays because they have a weird casting rule // that doesn't require the implemented interface to be variant to consider it castable. // For value types, we only need this when the array is castable by size (int[] and ICollection<uint>), // or it's a reference type (Derived[] and ICollection<Base>). TypeDesc elementType = providingType.IsArray ? ((ArrayType)providingType).ElementType : providingType.Instantiation[0]; result = CastingHelper.IsArrayElementTypeCastableBySize(elementType) || (elementType.IsDefType && !elementType.IsValueType); } return(result); }
bool IsAssignable(TypeDesc src, TypeDesc dst, bool allowSizeEquivalence = false) { if (src == dst) { return(true); } if (src.IsValueType || dst.IsValueType) { if (allowSizeEquivalence && IsSameReducedType(src, dst)) { return(true); } // TODO IsEquivalent return(false); } return(CastingHelper.CanCastTo(src, dst)); }
bool IsAssignable(StackValue src, StackValue dst) { if (src.Kind == dst.Kind && src.Type == dst.Type) { return(true); } if (dst.Type == null) { return(false); } switch (src.Kind) { case StackValueKind.ObjRef: if (dst.Kind != StackValueKind.ObjRef) { return(false); } // null is always assignable if (src.Type == null) { return(true); } return(CastingHelper.CanCastTo(src.Type, dst.Type)); case StackValueKind.ValueType: // TODO: Other cases - variance, etc. return(false); case StackValueKind.ByRef: // TODO: Other cases - variance, etc. return(false); case StackValueKind.Int32: return(dst.Kind == StackValueKind.Int64 || dst.Kind == StackValueKind.NativeInt); case StackValueKind.Int64: return(false); case StackValueKind.NativeInt: return(dst.Kind == StackValueKind.Int64); case StackValueKind.Float: return(false); default: // TODO: // return false; throw new NotImplementedException(); } #if false if (child == parent) { return(TRUE); } // Normally we just let the runtime sort it out but we wish to be more strict // than the runtime wants to be. For backwards compatibility, the runtime considers // int32[] and nativeInt[] to be the same on 32-bit machines. It also is OK with // int64[] and nativeInt[] on a 64-bit machine. if (child.IsType(TI_REF) && parent.IsType(TI_REF) && jitInfo->isSDArray(child.GetClassHandleForObjRef()) && jitInfo->isSDArray(parent.GetClassHandleForObjRef())) { BOOL runtime_OK; // never be more lenient than the runtime runtime_OK = jitInfo->canCast(child.m_cls, parent.m_cls); if (!runtime_OK) { return(false); } CORINFO_CLASS_HANDLE handle; CorInfoType pType = jitInfo->getChildType(child.GetClassHandleForObjRef(), &handle); CorInfoType cType = jitInfo->getChildType(parent.GetClassHandleForObjRef(), &handle); // don't care whether it is signed if (cType == CORINFO_TYPE_NATIVEUINT) { cType = CORINFO_TYPE_NATIVEINT; } if (pType == CORINFO_TYPE_NATIVEUINT) { pType = CORINFO_TYPE_NATIVEINT; } if (cType == CORINFO_TYPE_NATIVEINT) { return(pType == CORINFO_TYPE_NATIVEINT); } if (pType == CORINFO_TYPE_NATIVEINT) { return(cType == CORINFO_TYPE_NATIVEINT); } return(runtime_OK); } if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar()) { return(FALSE); // need to have had child == parent } else if (parent.IsType(TI_REF)) { // An uninitialized objRef is not compatible to initialized. if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef()) { return(FALSE); } if (child.IsNullObjRef()) // NULL can be any reference type { return(TRUE); } if (!child.IsType(TI_REF)) { return(FALSE); } return(jitInfo->canCast(child.m_cls, parent.m_cls)); } else if (parent.IsType(TI_METHOD)) { if (!child.IsType(TI_METHOD)) { return(FALSE); } // Right now we don't bother merging method handles return(FALSE); } else if (parent.IsType(TI_STRUCT)) { if (!child.IsType(TI_STRUCT)) { return(FALSE); } // Structures are compatible if they are equivalent return(jitInfo->areTypesEquivalent(child.m_cls, parent.m_cls)); } else if (parent.IsByRef()) { return(tiCompatibleWithByRef(jitInfo, child, parent)); } return(FALSE); #endif }
private void AddVirtualMethodUseDependencies(DependencyList dependencyList, NodeFactory factory) { if (_type.RuntimeInterfaces.Length > 0 && !factory.VTable(_type).HasFixedSlots) { DefType closestDefType = _type.GetClosestDefType(); foreach (var implementedInterface in _type.RuntimeInterfaces) { // If the type implements ICastable, the methods are implicitly necessary if (implementedInterface == factory.ICastableInterface) { MethodDesc isInstDecl = implementedInterface.GetKnownMethod("IsInstanceOfInterface", null); MethodDesc getImplTypeDecl = implementedInterface.GetKnownMethod("GetImplType", null); MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl); MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl); if (isInstMethodImpl != null) { dependencyList.Add(factory.VirtualMethodUse(isInstMethodImpl), "ICastable IsInst"); } if (getImplTypeMethodImpl != null) { dependencyList.Add(factory.VirtualMethodUse(getImplTypeMethodImpl), "ICastable GetImplType"); } } // If any of the implemented interfaces have variance, calls against compatible interface methods // could result in interface methods of this type being used (e.g. IEnumberable<object>.GetEnumerator() // can dispatch to an implementation of IEnumerable<string>.GetEnumerator()). // For now, we will not try to optimize this and we will pretend all interface methods are necessary. bool allInterfaceMethodsAreImplicitlyUsed = false; if (implementedInterface.HasVariance) { TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) { if (((GenericParameterDesc)interfaceDefinition.Instantiation[i]).Variance != 0 && !implementedInterface.Instantiation[i].IsValueType) { allInterfaceMethodsAreImplicitlyUsed = true; break; } } } if (!allInterfaceMethodsAreImplicitlyUsed && (_type.IsArray || _type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && implementedInterface.HasInstantiation) { // NOTE: we need to also do this for generic interfaces on arrays because they have a weird casting rule // that doesn't require the implemented interface to be variant to consider it castable. // For value types, we only need this when the array is castable by size (int[] and ICollection<uint>), // or it's a reference type (Derived[] and ICollection<Base>). TypeDesc elementType = _type.IsArray ? ((ArrayType)_type).ElementType : _type.Instantiation[0]; allInterfaceMethodsAreImplicitlyUsed = CastingHelper.IsArrayElementTypeCastableBySize(elementType) || (elementType.IsDefType && !elementType.IsValueType); } if (allInterfaceMethodsAreImplicitlyUsed) { foreach (var interfaceMethod in implementedInterface.GetAllMethods()) { if (interfaceMethod.Signature.IsStatic) { continue; } // Generic virtual methods are tracked by an orthogonal mechanism. if (interfaceMethod.HasInstantiation) { continue; } MethodDesc implMethod = closestDefType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); if (implMethod != null) { dependencyList.Add(factory.VirtualMethodUse(interfaceMethod), "Variant interface method"); dependencyList.Add(factory.VirtualMethodUse(implMethod), "Variant interface method"); } } } } } }
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); // Ensure that we track the necessary type symbol if we are working with a constructed type symbol. // The emitter will ensure we don't emit both, but this allows us assert that we only generate // relocs to nodes we emit. dependencyList.Add(factory.NecessaryTypeSymbol(_type), "NecessaryType for constructed type"); DefType closestDefType = _type.GetClosestDefType(); if (TrackInterfaceDispatchMapDepenendency && _type.RuntimeInterfaces.Length > 0) { dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map"); } if (_type.RuntimeInterfaces.Length > 0 && !factory.CompilationModuleGroup.ShouldProduceFullVTable(_type)) { foreach (var implementedInterface in _type.RuntimeInterfaces) { // If the type implements ICastable, the methods are implicitly necessary if (implementedInterface == factory.ICastableInterface) { MethodDesc isInstDecl = implementedInterface.GetKnownMethod("IsInstanceOfInterface", null); MethodDesc getImplTypeDecl = implementedInterface.GetKnownMethod("GetImplType", null); MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl); MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl); if (isInstMethodImpl != null) { dependencyList.Add(factory.VirtualMethodUse(isInstMethodImpl), "ICastable IsInst"); } if (getImplTypeMethodImpl != null) { dependencyList.Add(factory.VirtualMethodUse(getImplTypeMethodImpl), "ICastable GetImplType"); } } // If any of the implemented interfaces have variance, calls against compatible interface methods // could result in interface methods of this type being used (e.g. IEnumberable<object>.GetEnumerator() // can dispatch to an implementation of IEnumerable<string>.GetEnumerator()). // For now, we will not try to optimize this and we will pretend all interface methods are necessary. bool allInterfaceMethodsAreImplicitlyUsed = false; if (implementedInterface.HasVariance) { TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) { if (((GenericParameterDesc)interfaceDefinition.Instantiation[i]).Variance != 0 && !implementedInterface.Instantiation[i].IsValueType) { allInterfaceMethodsAreImplicitlyUsed = true; break; } } } if (!allInterfaceMethodsAreImplicitlyUsed && (_type.IsArray || _type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && implementedInterface.HasInstantiation) { // NOTE: we need to also do this for generic interfaces on arrays because they have a weird casting rule // that doesn't require the implemented interface to be variant to consider it castable. // For value types, we only need this when the array is castable by size (int[] and ICollection<uint>), // or it's a reference type (Derived[] and ICollection<Base>). TypeDesc elementType = _type.IsArray ? ((ArrayType)_type).ElementType : _type.Instantiation[0]; allInterfaceMethodsAreImplicitlyUsed = CastingHelper.IsArrayElementTypeCastableBySize(elementType) || (elementType.IsDefType && !elementType.IsValueType); } if (allInterfaceMethodsAreImplicitlyUsed) { foreach (var interfaceMethod in implementedInterface.GetAllMethods()) { if (interfaceMethod.Signature.IsStatic) { continue; } // Generic virtual methods are tracked by an orthogonal mechanism. if (interfaceMethod.HasInstantiation) { continue; } MethodDesc implMethod = closestDefType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); if (implMethod != null) { dependencyList.Add(factory.VirtualMethodUse(interfaceMethod), "Variant interface method"); dependencyList.Add(factory.VirtualMethodUse(implMethod), "Variant interface method"); } } } } } if (_type.IsArray) { // Array EEType depends on System.Array's virtuals. Array EETypes don't point to // their base type (i.e. there's no reloc based dependency making this "just work"). dependencyList.Add(factory.ConstructedTypeSymbol(_type.BaseType), "Array base type"); } dependencyList.Add(factory.VTable(_type), "VTable"); if (closestDefType.HasGenericDictionarySlot()) { // Add a dependency on the template for this type, if the canonical type should be generated into this binary. DefType templateType = GenericTypesTemplateMap.GetActualTemplateTypeForType(factory, _type.ConvertToCanonForm(CanonicalFormKind.Specific)); if (templateType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(templateType).RepresentsIndirectionCell) { dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(templateType), "Template Type Layout"); } } // Generated type contains generic virtual methods that will get added to the GVM tables if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) { dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); } if (factory.TypeSystemContext.HasLazyStaticConstructor(_type)) { // The fact that we generated an EEType means that someone can call RuntimeHelpers.RunClassConstructor. // We need to make sure this is possible. dependencyList.Add(new DependencyListEntry(factory.TypeNonGCStaticsSymbol((MetadataType)_type), "Class constructor")); } return(dependencyList); }