| File: Compiler\DependencyAnalysis\VariantInterfaceMethodUseNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using ILCompiler.DependencyAnalysisFramework; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { // This node represents the concept of a variant interface method being used. // It has no direct dependencies, but may be referred to by conditional static // dependencies, or static dependencies from elsewhere. // // We only track the generic definition of the interface method being used. // There's a potential optimization opportunity because e.g. the fact that // IEnumerable<string>.GetEnumerator() is used doesn't mean that // IEnumerable<char>.GetEnumerator() is used, but this complicates the tracking. internal sealed class VariantInterfaceMethodUseNode : DependencyNodeCore<NodeFactory> { private readonly MethodDesc _decl; public VariantInterfaceMethodUseNode(MethodDesc decl) { Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); Debug.Assert(decl.IsVirtual); Debug.Assert(decl.OwningType.IsInterface); // Virtual method use always represents the slot defining method of the virtual. // Places that might see virtual methods being used through an override need to normalize // to the slot defining method. Debug.Assert(MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(decl) == decl); // Generic virtual methods are tracked by an orthogonal mechanism. Debug.Assert(!decl.HasInstantiation); // We currently track this for definitions only Debug.Assert(decl.IsTypicalMethodDefinition); _decl = decl; } 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.TypeSystemContext.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; } 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; } protected override string GetName(NodeFactory factory) => $"VariantInterfaceMethodUse {_decl}"; public override bool HasConditionalStaticDependencies => false; public override bool HasDynamicDependencies => false; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool StaticDependenciesAreComputed => true; public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) => null; public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null; } }