// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using ILCompiler.Dataflow; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { internal class TypeValidationChecker { private ConcurrentBag<(TypeDesc type, string reason)> _typeLoadValidationErrors = new ConcurrentBag<(TypeDesc type, string reason)>(); private ConcurrentDictionary<TypeDesc, Task<bool>> _firstStageValidationChecks = new ConcurrentDictionary<TypeDesc, Task<bool>>(); private ConcurrentDictionary<TypeDesc, (TypeDesc dependendOnType, string reason)[]> _crossTypeValidationDependencies = new ConcurrentDictionary<TypeDesc, (TypeDesc dependendOnType, string reason)[]>(); private ConcurrentQueue<Task<bool>> _tasksThatMustFinish = new ConcurrentQueue<Task<bool>>(); private TypeValidationChecker() { } private void LogErrors(Action<string> loggingFunction) { var typeLoadValidationErrors = _typeLoadValidationErrors.ToArray(); Array.Sort(typeLoadValidationErrors, (ValueTuple<TypeDesc, string> left, ValueTuple<TypeDesc, string> right) => { return TypeSystemComparer.Instance.Compare(left.Item1, right.Item1); }); loggingFunction($"{_typeLoadValidationErrors.Count} type validation errors"); loggingFunction("------"); foreach (var reason in typeLoadValidationErrors) { loggingFunction($"{reason.type}: {reason.reason}"); } loggingFunction("------"); } private void AddTypeValidationError(TypeDesc type, string error) { _typeLoadValidationErrors.Add((type, error)); } private async Task<bool> CanSkipValidationInstance(EcmaModule module) { // The system module can always skip type validation #if !DEBUG if (module == module.Context.SystemModule) return true; #endif foreach (var type in module.GetAllTypes()) { if (type is EcmaType ecmaType) _tasksThatMustFinish.Enqueue(ValidateType(this, ecmaType)); } _tasksThatMustFinish.Enqueue(ValidateType(this, module.GetGlobalModuleType())); bool failAtEnd = false; while (_tasksThatMustFinish.TryDequeue(out var taskToComplete)) { if (!await taskToComplete) failAtEnd = true; } #if DEBUG if (module == module.Context.SystemModule) { // Spot check that the system module ALWAYS succeeds if (failAtEnd) { throw new InternalCompilerErrorException("System module failed to validate all types"); } } #endif return !failAtEnd; } public static async Task<(bool canSkipValidation, string[] reasonsWhyItFailed)> CanSkipValidation(EcmaModule module) { TypeValidationChecker checker = new TypeValidationChecker(); bool canSkipValidation = await checker.CanSkipValidationInstance(module); List<string> reasons = new List<string>(); checker.LogErrors(reasons.Add); return (canSkipValidation, reasons.ToArray()); } private static Task<bool> ValidateType(TypeValidationChecker checker, EcmaType type) { if (checker._firstStageValidationChecks.TryGetValue(type, out var result)) return result; Task<bool> skipValidatorForType = Task.Run(() => checker.ValidateTypeWorker(type)); checker._firstStageValidationChecks.TryAdd(type, skipValidatorForType); return skipValidatorForType; } private async Task<bool> ValidateTypeWorker(EcmaType type) { Task<bool> ValidateTypeWorkerHelper(TypeDesc typeToCheckForSkipValidation) { return ValidateTypeHelper(typeToCheckForSkipValidation.InstantiateSignature(type.Instantiation, new Instantiation())); } // The runtime has a number of checks in the type loader which it will skip running if the SkipValidation flag is set // This function attempts to document all of them, and implement *some* of them. // This function performs a portion of the validation skipping that has been found to have some importance, or to serve as // In addition, there are comments about all validation skipping activities that the runtime will perform. try { var typeDef = type.MetadataReader.GetTypeDefinition(type.Handle); // Validate that the base type is loadable if (type.BaseType != null) { if (!await ValidateTypeWorkerHelper(type.BaseType)) { AddTypeValidationError(type, "BaseType failed validation"); return false; } } // Validate that the base type is accessible to this type -- UNIMPLEMENTED foreach (var interfaceType in type.RuntimeInterfaces) { // Validate that the all referenced interface types are loadable if (!await ValidateTypeWorkerHelper(interfaceType)) { AddTypeValidationError(type, $"Interface type {interfaceType} failed validation"); return false; } if (!interfaceType.IsInterface) { AddTypeValidationError(type, $"Runtime interface {interfaceType} is not an interface"); return false; } if (interfaceType.HasInstantiation) { // Interfaces behave covariantly ValidateVarianceInType(type.Instantiation, interfaceType, Internal.TypeSystem.GenericVariance.Covariant); } } // Validate that each interface type explicitly implemented on this type is accessible to this type -- UNIMPLEMENTED foreach (var field in type.GetFields()) { // Validate that all fields on the type are both loadable if (!await ValidateTypeWorkerHelper(field.FieldType)) { AddTypeValidationError(type, $"Field {field.GetName()}'s type failed validation"); return false; } // Validate that all fields on the type are accessible -- UNIMPLEMENTED } // Per method rules foreach (var methodDesc in type.GetMethods()) { var method = methodDesc; var methodDef = method.MetadataReader.GetMethodDefinition(method.Handle); // Validate that the validateTokenSig algorithm on all methods defined on the type // The validateTokenSig algorithm simply validates the phyical structure of the signature. Getting a MethodSignature object is a more complete check try { var getSignature = method.Signature; } catch { AddTypeValidationError(type, $"Signature could not be loaded for method {method.GetName()}"); return false; } // Validate that enums have no methods if (type.IsEnum) { AddTypeValidationError(type, $"Is enum type with a method"); return false; } // Validate that if the method has an RVA that (the Class is not a ComImport class, it is not abstract, it is not marked with the miRuntime flag, and is not marked as InternalCall) if (methodDef.RelativeVirtualAddress != 0) { // Validate that if the method has an RVA that the Class is not a ComImport class -- UNIMPLEMENTED // Validate that if the method has an RVA that the method is not abstract if (methodDef.Attributes.HasFlag(System.Reflection.MethodAttributes.Abstract)) { AddTypeValidationError(type, $"{method} is an abstract method with a non-zero RVA"); return false; } // Validate that if the method has an RVA is not marked with the miRuntime flag if (methodDef.ImplAttributes.HasFlag(System.Reflection.MethodImplAttributes.Runtime)) { AddTypeValidationError(type, $"{method} is an miRuntime method with a non-zero RVA"); return false; } // Validate that if the method has an RVA is not marked as InternalCall if (methodDef.ImplAttributes.HasFlag(System.Reflection.MethodImplAttributes.InternalCall)) { AddTypeValidationError(type, $"{method} is an internal call method with a non-zero RVA"); return false; } } // Validate that abstract methods cannot exist on non-abstract classes if (method.IsAbstract && !type.IsAbstract) { AddTypeValidationError(type, $"abstract method {method} defined on non-abstract type"); return false; } // Validate that for instance methods, the abstract flag can only be set on a virtual method. if (!methodDef.Attributes.HasFlag(MethodAttributes.Static) && method.IsAbstract) { if (!method.IsVirtual) { AddTypeValidationError(type, $"instance abstract method {method} not marked as virtual"); return false; } } // Validate that interfaces can only have rtSpecialName methods which are "_VtblGap" or ".cctor" methods if (type.IsInterface) { if (methodDef.Attributes.HasFlag(MethodAttributes.RTSpecialName)) { if (!method.Name.SequenceEqual(".cctor"u8) && !method.Name.StartsWith("_VtblGap"u8)) { AddTypeValidationError(type, $"Special name method {method} defined on interface"); return false; } } } if (method.IsVirtual) { // Validate that if a method is virtual that it cannot be a p/invoke if (method.IsPInvoke) { AddTypeValidationError(type, $"'{method}' is both virtual and a p/invoke"); return false; } // Validate that if a method is virtual and static it can only exist on an interface if (methodDef.Attributes.HasFlag(MethodAttributes.Static) && !type.IsInterface) { AddTypeValidationError(type, $"'{method}' is a virtual static method not defined on an interface"); return false; } // Validate that constructors cannot be marked as virtual if (method.IsConstructor || method.IsStaticConstructor) { AddTypeValidationError(type, $"'{method}' is a virtual constructor"); return false; } } // Validate that no synchronized methods may exist on a value type if (type.IsValueType) { if (method.IsSynchronized) { AddTypeValidationError(type, $"'{method}' is synchronized method on a value type"); return false; } } // validate that the global class cannot have instance methods if (type.Module.GetGlobalModuleType() == type && !methodDef.Attributes.HasFlag(MethodAttributes.Static)) { AddTypeValidationError(type, $"'{method}' is an instance method defined on the global <module> type"); return false; } // Validate that a generic method cannot be on a ComImport class, or a ComEventInterface -- UNIMPLEMENTED // Validate that a generic method cannot be a p/invoke if (method.IsPInvoke) { if (type.HasInstantiation) { AddTypeValidationError(type, $"'{method}' is an pinvoke defined on a generic type"); return false; } if (method.HasInstantiation) { AddTypeValidationError(type, $"'{method}' is an pinvoke defined as a generic method"); return false; } } // Validate that outside of CoreLib, that a generic method cannot be an internal call method if (type.Context.SystemModule != type.Module && method.IsInternalCall) { if (method.HasInstantiation) { AddTypeValidationError(type, $"'{method}' is an internal call generic method"); return false; } if (type.HasInstantiation) { AddTypeValidationError(type, $"'{method}' is an internal call method on generic type"); return false; } } // Validate that a generic method cannot be marked as runtime if (method.HasInstantiation && method.IsRuntimeImplemented) { AddTypeValidationError(type, $"'{method}' is an runtime-impl generic method"); return false; } // Validate that generic variance is properly respected in method signatures if (type.IsInterface && method.IsVirtual && !method.Signature.IsStatic && type.HasInstantiation) { if (!ValidateVarianceInSignature(type.Instantiation, method.Signature, Internal.TypeSystem.GenericVariance.Covariant, Internal.TypeSystem.GenericVariance.Contravariant)) { AddTypeValidationError(type, $"'{method}' has invalid variance in signature"); return false; } } foreach (var genericParameterType in method.Instantiation) { foreach (var constraint in ((GenericParameterDesc)genericParameterType).TypeConstraints) { if (!ValidateVarianceInType(type.Instantiation, constraint, Internal.TypeSystem.GenericVariance.Contravariant)) { AddTypeValidationError(type, $"'{method}' has invalid variance in generic parameter constraint {constraint} on type {genericParameterType}"); return false; } } } if (!BoundedCheckForInstantiation(method.Instantiation)) { AddTypeValidationError(type, $"'{method}' has cyclical generic parameter constraints"); return false; } // Validate that constraints are all acccessible to the method using them -- UNIMPLEMENTED } // Generic class special rules // Validate that a generic class cannot be a ComImport class, or a ComEventInterface class -- UNIMPLEMENTED foreach (var genericParameterType in type.Instantiation) { foreach (var constraint in ((GenericParameterDesc)genericParameterType).TypeConstraints) { if (!ValidateVarianceInType(type.Instantiation, constraint, Internal.TypeSystem.GenericVariance.Contravariant)) { AddTypeValidationError(type, $"'{genericParameterType}' has invalid variance in generic parameter constraint {constraint}"); return false; } } } // Validate that generic variance is properly respected in interface signatures if (type.HasInstantiation && type.IsInterface) { foreach (var interfaceType in type.ExplicitlyImplementedInterfaces) { if (interfaceType.HasInstantiation) { // Interfaces behave covariantly if (!ValidateVarianceInType(type.Instantiation, interfaceType, Internal.TypeSystem.GenericVariance.Covariant)) { AddTypeValidationError(type, $"'{type}' has invalid variance in in interface impl of {interfaceType}"); return false; } } } } // Validate that there are no cyclical class or method constraints, and that constraints are all acccessible to the type using them if (!BoundedCheckForInstantiation(type.Instantiation)) { AddTypeValidationError(type, $"'{type}' has cyclical generic parameter constraints"); return false; } // Override rules // Validate that each override results does not violate accessibility rules -- UNIMPLEMENTED HashSet<MethodDesc> overriddenDeclMethods = new HashSet<MethodDesc>(); foreach (var methodImplHandle in typeDef.GetMethodImplementations()) { var methodImpl = type.MetadataReader.GetMethodImplementation(methodImplHandle); var methodBody = type.Module.GetMethod(methodImpl.MethodBody); var methodDecl = type.Module.GetMethod(methodImpl.MethodDeclaration); // Validate that all MethodImpls actually match signatures closely enough if (!methodBody.Signature.ApplySubstitution(type.Instantiation).EquivalentWithCovariantReturnType(methodDecl.Signature.ApplySubstitution(type.Instantiation))) { AddTypeValidationError(type, $"MethodImpl with Body '{methodBody}' and Decl '{methodDecl}' do not have matching signatures"); return false; } if (!methodDecl.IsVirtual) { AddTypeValidationError(type, $"MethodImpl with Decl '{methodDecl}' points at non-virtual decl method"); return false; } if (methodDecl.IsFinal) { AddTypeValidationError(type, $"MethodImpl with Decl '{methodDecl}' points at sealed decl method"); return false; } bool isStatic = methodBody.Signature.IsStatic; if (methodBody.OwningType.IsInterface && !isStatic && !methodBody.IsFinal) { AddTypeValidationError(type, $"MethodImpl with Body '{methodBody}' and Decl '{methodDecl}' implements interface on another interface with a non-sealed method"); return false; } if (isStatic && methodBody.IsVirtual) { AddTypeValidationError(type, $"MethodImpl with Body '{methodBody}' and Decl '{methodDecl}' implements a static virtual method with a virtual static method"); return false; } if (!isStatic && !methodBody.IsVirtual) { AddTypeValidationError(type, $"MethodImpl with Body '{methodBody}' and Decl '{methodDecl}' implements a instance virtual method with a non-virtual instance method"); return false; } // Validate that multiple MethodImpls don't override the same method if (!overriddenDeclMethods.Add(methodDecl)) { AddTypeValidationError(type, $"Multiple MethodImpl records override '{methodDecl}'"); return false; } // Validate that the MethodImpl follows MethodImpl accessibility rules -- UNIMPLEMENTED } var virtualMethodAlgorithm = type.Context.GetVirtualMethodAlgorithmForType(type); VirtualMethodAlgorithm baseTypeVirtualMethodAlgorithm = null; if (type.BaseType != null && !type.IsInterface && !type.IsValueType) { baseTypeVirtualMethodAlgorithm = type.Context.GetVirtualMethodAlgorithmForType(type.BaseType); } foreach (var interfaceImplemented in type.RuntimeInterfaces) { foreach (var interfaceMethod in interfaceImplemented.GetVirtualMethods()) { MethodDesc resolvedMethod; bool staticInterfaceMethod = interfaceMethod.Signature.IsStatic; if (staticInterfaceMethod) { resolvedMethod = virtualMethodAlgorithm.ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, type); } else { resolvedMethod = type.ResolveInterfaceMethodTarget(interfaceMethod); } if (resolvedMethod != null) { // Validate that for every override involving generic methods that the generic method constraints are matching if (!CompareMethodConstraints(interfaceMethod, resolvedMethod)) { AddTypeValidationError(type, $"Interface method '{interfaceMethod}' overridden by method '{resolvedMethod}' which does not have matching generic constraints"); return false; } } // Validate that all virtual static methods are actually implemented if the type is not abstract // Validate that all virtual instance methods are actually implemented if the type is not abstract if (!type.IsAbstract) { if (null == resolvedMethod || (staticInterfaceMethod && resolvedMethod.IsAbstract)) { if (virtualMethodAlgorithm.ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, type, out var impl) != DefaultInterfaceMethodResolution.DefaultImplementation || impl.IsAbstract) { AddTypeValidationError(type, $"Interface method '{interfaceMethod}' does not have implementation"); return false; } if (impl != null) { // Validate that for every override involving generic methods that the generic method constraints are matching if (!CompareMethodConstraints(interfaceMethod, impl)) { AddTypeValidationError(type, $"Interface method '{interfaceMethod}' overridden by method '{impl}' which does not have matching generic constraints"); return false; } } } } } } if (!type.IsInterface) { foreach (var virtualMethod in type.EnumAllVirtualSlots()) { var implementationMethod = virtualMethodAlgorithm.FindVirtualFunctionTargetMethodOnObjectType(virtualMethod, type); if (implementationMethod != null) { // Validate that for every override involving generic methods that the generic method constraints are matching if (!CompareMethodConstraints(virtualMethod, implementationMethod)) { AddTypeValidationError(type, $"Virtual method '{virtualMethod}' overridden by method '{implementationMethod}' which does not have matching generic constraints"); return false; } // Validate that if the decl method for the virtual is not on the immediate base type, that the intermediate type did not establish a // covariant return type which requires the implementation method to specify a more specific base type if ((virtualMethod.OwningType != type.BaseType) && (virtualMethod.OwningType != type) && (baseTypeVirtualMethodAlgorithm != null)) { var implementationOnBaseType = baseTypeVirtualMethodAlgorithm.FindVirtualFunctionTargetMethodOnObjectType(virtualMethod, type.BaseType); if (!implementationMethod.Signature.ApplySubstitution(type.Instantiation).EquivalentWithCovariantReturnType(implementationOnBaseType.Signature.ApplySubstitution(type.Instantiation))) { AddTypeValidationError(type, $"Virtual method '{virtualMethod}' overridden by method '{implementationMethod}' does not satisfy the covariant return type introduced with '{implementationOnBaseType}'"); return false; } } } // Validate that all virtual static methods are actually implemented if the type is not abstract // Validate that all virtual instance methods are actually implemented if the type is not abstract if (!type.IsAbstract) { if (implementationMethod == null || implementationMethod.IsAbstract) { AddTypeValidationError(type, $"Interface method '{virtualMethod}' does not have implementation"); return false; } } } } if (type.TypeIdentifierData != null) { if (!type.TypeHasCharacteristicsRequiredToBeLoadableTypeEquivalentType) { return false; } } return true; } catch (Exception ex) { // If we throw an exception, clearly type validation skipping was not to be AddTypeValidationError(type, $"due to exception '{ex.ToString()}'"); return false; } static bool CompareGenericParameterConstraint(MethodDesc declMethod, GenericParameterDesc parameterOfDecl, MethodDesc implMethod, GenericParameterDesc parameterOfImpl) { if (parameterOfImpl.HasDefaultConstructorConstraint) if (!parameterOfDecl.HasDefaultConstructorConstraint && !parameterOfDecl.HasNotNullableValueTypeConstraint) return false; if (parameterOfImpl.HasNotNullableValueTypeConstraint) if (!parameterOfDecl.HasNotNullableValueTypeConstraint) return false; if (parameterOfImpl.HasReferenceTypeConstraint) if (!parameterOfDecl.HasReferenceTypeConstraint) return false; // Constraints that 'allow' must check the impl first if (parameterOfImpl.HasAllowByRefLikeConstraint) if (!parameterOfDecl.HasAllowByRefLikeConstraint) return false; HashSet<TypeDesc> constraintsOnDecl = new HashSet<TypeDesc>(); foreach (var constraint in parameterOfDecl.TypeConstraints) { constraintsOnDecl.Add(constraint.InstantiateSignature(declMethod.OwningType.Instantiation, implMethod.Instantiation).InstantiateSignature(implMethod.OwningType.Instantiation, new Instantiation())); } foreach (var constraint in parameterOfImpl.TypeConstraints) { if (!constraintsOnDecl.Contains(constraint.InstantiateSignature(implMethod.OwningType.Instantiation, implMethod.Instantiation))) return false; } return true; } static bool CompareMethodConstraints(MethodDesc methodDecl, MethodDesc methodImpl) { // Validate that methodDecl's method constraints are at least as stringent as methodImpls // The Decl is permitted to be more stringent. if (methodDecl.Instantiation.Length != methodImpl.Instantiation.Length) return false; for (int i = 0; i < methodDecl.Instantiation.Length; i++) { var genericParameterDescOnImpl = (GenericParameterDesc)methodImpl.GetTypicalMethodDefinition().Instantiation[i]; var genericParameterDescOnDecl = (GenericParameterDesc)methodDecl.GetTypicalMethodDefinition().Instantiation[i]; if (!CompareGenericParameterConstraint(methodDecl, genericParameterDescOnDecl, methodImpl, genericParameterDescOnImpl)) { return false; } } return true; } Task<bool> ValidateTypeHelper(TypeDesc typeDesc) { if (typeDesc == null) return Task.FromResult(true); if (typeDesc is EcmaType ecmaType) { // Trigger the ecmaType to have its type checked, but do not check the task immediately. Unfortunately this can be recursive. _tasksThatMustFinish.Enqueue(ValidateType(this, ecmaType)); return Task.FromResult(true); } else if (typeDesc is InstantiatedType instantiatedType) return ValidateTypeHelperInstantiatedType(instantiatedType); else if (typeDesc is ParameterizedType parameterizedType) return ValidateTypeHelper(parameterizedType.ParameterType); else if (typeDesc is FunctionPointerType functionPointerType) return ValidateTypeHelperFunctionPointerType(functionPointerType); return Task.FromResult(true); } Task<bool> ValidateTypeHelperInstantiatedType(InstantiatedType instantiatedType) { try { // Constraints should be satisfied if (!instantiatedType.CheckConstraints()) { AddTypeValidationError(type, $"Constraint check failed validating {instantiatedType}"); return Task.FromResult(false); } return ValidateTypeHelper(instantiatedType.GetTypeDefinition()); } catch (Exception ex) { AddTypeValidationError(instantiatedType, $"due to exception '{ex}'"); return Task.FromResult(false); } } async Task<bool> ValidateTypeHelperFunctionPointerType(FunctionPointerType functionPointerType) { if (!await ValidateTypeHelper(functionPointerType.Signature.ReturnType)) return false; foreach (var type in functionPointerType.Signature) { if (!await ValidateTypeHelper(type)) return false; } return true; } static bool BoundedCheckForInstantiation(Instantiation instantiation) { foreach (var type in instantiation) { Debug.Assert(type.IsGenericParameter); GenericParameterDesc genericParameter = (GenericParameterDesc)type; if (!BoundedCheckForType(genericParameter, instantiation.Length)) return false; } return true; } static bool BoundedCheckForType(GenericParameterDesc genericParameter, int maxDepth) { if (maxDepth <= 0) return false; foreach (var type in genericParameter.TypeConstraints) { if (type is GenericParameterDesc possiblyCircularGenericParameter) { if (possiblyCircularGenericParameter.Kind == genericParameter.Kind) { if (!BoundedCheckForType(possiblyCircularGenericParameter, maxDepth - 1)) return false; } } } return true; } static bool ValidateVarianceInSignature(Instantiation associatedTypeInstantiation, MethodSignature signature, Internal.TypeSystem.GenericVariance returnTypePosition, Internal.TypeSystem.GenericVariance argTypePosition) { for (int i = 0; i < signature.Length; i++) { if (!ValidateVarianceInType(associatedTypeInstantiation, signature[i], argTypePosition)) return false; } if (!ValidateVarianceInType(associatedTypeInstantiation, signature.ReturnType, returnTypePosition)) return false; return true; } static bool ValidateVarianceInType(Instantiation associatedTypeInstantiation, TypeDesc type, Internal.TypeSystem.GenericVariance position) { if (type is SignatureTypeVariable signatureTypeVar) { GenericParameterDesc gp = (GenericParameterDesc)associatedTypeInstantiation[signatureTypeVar.Index]; if (gp.Variance != Internal.TypeSystem.GenericVariance.None) { if (position != gp.Variance) { // Covariant and contravariant parameters can *only* appear in resp. covariant and contravariant positions return false; } } } else if (type is InstantiatedType instantiatedType) { var typeDef = instantiatedType.GetTypeDefinition(); if (typeDef.IsValueType || position == Internal.TypeSystem.GenericVariance.None) { // Value types and non-variant positions require all parameters to be non-variant foreach (TypeDesc instType in instantiatedType.Instantiation) { if (!ValidateVarianceInType(associatedTypeInstantiation, instType, Internal.TypeSystem.GenericVariance.None)) return false; } } else { int index = 0; for (index = 0; index < typeDef.Instantiation.Length; index++) { Internal.TypeSystem.GenericVariance positionForParameter = ((GenericParameterDesc)typeDef.Instantiation[index]).Variance; // If the surrounding context is contravariant then we need to flip the variance of this parameter if (position == Internal.TypeSystem.GenericVariance.Contravariant) { if (positionForParameter == Internal.TypeSystem.GenericVariance.Covariant) positionForParameter = Internal.TypeSystem.GenericVariance.Contravariant; else if (positionForParameter == Internal.TypeSystem.GenericVariance.Contravariant) positionForParameter = Internal.TypeSystem.GenericVariance.Covariant; else positionForParameter = Internal.TypeSystem.GenericVariance.None; } if (!ValidateVarianceInType(associatedTypeInstantiation, instantiatedType.Instantiation[index], positionForParameter)) return false; } } } else if (type is ParameterizedType parameterizedType) { // Arrays behave as covariant, byref and pointer types are non-variant if (!ValidateVarianceInType(associatedTypeInstantiation, parameterizedType.ParameterType, (parameterizedType.IsByRef || parameterizedType.IsPointer) ? Internal.TypeSystem.GenericVariance.None : position)) return false; } else if (type is FunctionPointerType functionPointerType) { if (!ValidateVarianceInSignature(associatedTypeInstantiation, functionPointerType.Signature, Internal.TypeSystem.GenericVariance.None, Internal.TypeSystem.GenericVariance.None)) return false; } return true; } } } } |