|
// 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;
}
}
}
}
|