GVM dependency analysis performance fixes (#3872)

Refactor GVM algorithm for better performance
Fix GVM resolution bug on derived types
Fix bug with variance (https://github.com/dotnet/corert/issues/3454)
This commit is contained in:
Fadi Hanna 2017-06-14 18:28:04 -07:00 коммит произвёл GitHub
Родитель c531b93451
Коммит 962401ec63
10 изменённых файлов: 310 добавлений и 98 удалений

Просмотреть файл

@ -209,6 +209,8 @@ namespace Internal.TypeSystem
// Step 4, name/sig match virtual function resolve
MethodDesc resolutionTarget = FindNameSigOverrideForVirtualMethod(group.DefiningMethod, uninstantiatedType);
if (resolutionTarget == null)
return null;
// Step 5, convert resolution target from uninstantiated form target to objecttype target,
// and instantiate as appropriate

Просмотреть файл

@ -47,6 +47,22 @@ namespace Internal.TypeSystem
return true;
}
public static bool CheckValidInstantiationArguments(this Instantiation instantiation)
{
foreach(var arg in instantiation)
{
if (arg.IsPointer || arg.IsByRef || arg.IsGenericParameter || arg.IsRuntimeDeterminedSubtype || arg.IsVoid)
return false;
if (arg.HasInstantiation)
{
if (!CheckValidInstantiationArguments(arg.Instantiation))
return false;
}
}
return true;
}
public static bool CheckConstraints(this TypeDesc type)
{
TypeDesc uninstantiatedType = type.GetTypeDefinition();

Просмотреть файл

@ -29,7 +29,43 @@ namespace ILCompiler.DependencyAnalysis
{
get
{
return _type.IsDefType && _type.HasGenericVirtualMethod();
if (_type.IsInterface)
return _type.HasGenericVirtualMethods();
if (_type.IsDefType)
{
// First, check if this type has any GVM that overrides a GVM on a parent type. If that's the case, this makes
// the current type interesting for GVM analysis (i.e. instantiate its overriding GVMs for existing GVMDependenciesNodes
// of the instantiated GVM on the parent types).
foreach (var method in _type.GetAllMethods())
{
if (method.HasInstantiation && method.IsVirtual)
{
MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
if (slotDecl != method)
return true;
}
}
// Second, check if this type has any GVMs that implement any GVM on any of the implemented interfaces. This would
// make the current type interesting for dynamic dependency analysis to that we can instantiate its GVMs.
foreach (DefType interfaceImpl in _type.RuntimeInterfaces)
{
foreach (var method in interfaceImpl.GetAllMethods())
{
if (method.HasInstantiation && method.IsVirtual)
{
// We found a GVM on one of the implemented interfaces. Find if the type implements this method.
// (Note, do this comparision against the generic definition of the method, not the specific method instantiation
MethodDesc genericDefinition = method.GetMethodDefinition();
MethodDesc slotDecl = _type.ResolveInterfaceMethodTarget(genericDefinition);
if (slotDecl != null)
return true;
}
}
}
}
return false;
}
}

Просмотреть файл

@ -20,7 +20,7 @@ namespace ILCompiler.DependencyAnalysis
/// </summary>
internal class GVMDependenciesNode : DependencyNodeCore<NodeFactory>
{
private MethodDesc _method;
private readonly MethodDesc _method;
public MethodDesc Method => _method;
@ -41,14 +41,45 @@ namespace ILCompiler.DependencyAnalysis
// TODO: https://github.com/dotnet/corert/issues/3224
if (_method.IsAbstract)
{
return new DependencyListEntry[]
{
new DependencyListEntry(context.ReflectableMethod(_method), "Abstract reflectable method"),
};
yield return new DependencyListEntry(context.ReflectableMethod(_method), "Abstract reflectable method");
}
else
{
MethodDesc instantiatedMethod = _method;
return Array.Empty<DependencyListEntry>();
// Universal canonical instantiations should be entirely universal canon
if (instantiatedMethod.IsCanonicalMethod(CanonicalFormKind.Universal))
instantiatedMethod = instantiatedMethod.GetCanonMethodTarget(CanonicalFormKind.Universal);
bool validInstantiation =
instantiatedMethod.IsSharedByGenericInstantiations || ( // Non-exact methods are always valid instantiations (always pass constraints check)
instantiatedMethod.Instantiation.CheckValidInstantiationArguments() &&
instantiatedMethod.OwningType.Instantiation.CheckValidInstantiationArguments() &&
instantiatedMethod.CheckConstraints());
if (validInstantiation)
{
MethodDesc canonMethodTarget = instantiatedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
bool getUnboxingStub = instantiatedMethod.OwningType.IsValueType;
yield return new DependencyListEntry(context.MethodEntrypoint(canonMethodTarget, getUnboxingStub), "GVM Dependency - Canon method");
if (canonMethodTarget != instantiatedMethod)
{
// Dependency includes the generic method dictionary of the instantiation, and all its dependencies. This is done by adding the
// ShadowConcreteMethod to the list of dynamic dependencies. The generic dictionary will be reported as a dependency of the ShadowConcreteMethod
// TODO: detect large recursive generics and fallback to USG templates
Debug.Assert(!instantiatedMethod.IsCanonicalMethod(CanonicalFormKind.Any));
yield return new DependencyListEntry(context.ShadowConcreteMethod(instantiatedMethod), "GVM Dependency - Dictionary");
}
}
else
{
// TODO: universal generics
}
}
}
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context)
{
return Array.Empty<CombinedDependencyListEntry>();
@ -90,20 +121,25 @@ namespace ILCompiler.DependencyAnalysis
continue;
TypeDesc potentialOverrideType = entryAsEETypeNode.Type;
if (!(potentialOverrideType is DefType))
if (!potentialOverrideType.IsDefType)
continue;
Debug.Assert(!potentialOverrideType.IsRuntimeDeterminedSubtype);
if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && potentialOverrideType.IsInterface && (potentialOverrideType != _method.OwningType))
if (potentialOverrideType.IsInterface)
{
if (_method.OwningType.CanCastTo(potentialOverrideType))
if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && (potentialOverrideType != _method.OwningType))
{
// Variance expansion
MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation);
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency"));
if (potentialOverrideType.CanCastTo(_method.OwningType))
{
// Variance expansion
MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation);
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency"));
}
}
continue;
}
// If this is an interface gvm, look for types that implement the interface
@ -112,89 +148,59 @@ namespace ILCompiler.DependencyAnalysis
// relationship in the vtable.
if (_method.OwningType.IsInterface)
{
if (potentialOverrideType.IsInterface)
continue;
foreach (DefType interfaceImpl in potentialOverrideType.RuntimeInterfaces)
{
if (interfaceImpl.ConvertToCanonForm(CanonicalFormKind.Specific) == _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))
if (interfaceImpl == _method.OwningType)
{
// Find if the type implements this method. (Note, do this comparision against the generic definition of the method, not the
// specific method instantiation that is "method"
MethodDesc genericDefinition = interfaceImpl.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(genericDefinition);
MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(_method.GetMethodDefinition());
if (slotDecl != null)
CreateDependencyForMethodSlotAndInstantiation(slotDecl, dynamicDependencies, factory);
{
MethodDesc implementingMethodInstantiation = _method.Context.GetInstantiatedMethod(slotDecl, _method.Instantiation);
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation), null, "ImplementingMethodInstantiation"));
}
}
}
}
else
{
// TODO: Ensure GVM Canon Target
TypeDesc overrideTypeCanonCur = potentialOverrideType;
TypeDesc methodCanonContainingType = _method.OwningType;
while (overrideTypeCanonCur != null)
// Quickly check if the potential overriding type is at all related to the GVM's owning type (there is no need
// to do any processing for a type that is not at all related to the GVM's owning type. Resolving virtuals is expensive).
TypeDesc overrideTypeCur = potentialOverrideType;
{
if (overrideTypeCanonCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodCanonContainingType.ConvertToCanonForm(CanonicalFormKind.Specific))
do
{
MethodDesc methodDefInDerivedType = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
if(methodDefInDerivedType != null)
CreateDependencyForMethodSlotAndInstantiation(methodDefInDerivedType, dynamicDependencies, factory);
if (overrideTypeCur == _method.OwningType)
break;
MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method);
if (slotDecl != null)
CreateDependencyForMethodSlotAndInstantiation(slotDecl.GetMethodDefinition(), dynamicDependencies, factory);
overrideTypeCur = overrideTypeCur.BaseType;
}
while (overrideTypeCur != null);
if (overrideTypeCur == null)
continue;
}
overrideTypeCur = potentialOverrideType;
while (overrideTypeCur != null)
{
if (overrideTypeCur == _method.OwningType)
{
// The GVMDependencyNode already declares the entrypoint/dictionary dependencies of the current method
// as static dependencies, therefore we can break the loop as soon we hit the current method's owning type
// while we're traversing the hierarchy of the potential derived types.
break;
}
overrideTypeCanonCur = overrideTypeCanonCur.BaseType;
MethodDesc instantiatedTargetMethod = overrideTypeCur.FindVirtualFunctionTargetMethodOnObjectType(_method);
if (instantiatedTargetMethod != null)
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(instantiatedTargetMethod), null, "DerivedMethodInstantiation"));
overrideTypeCur = overrideTypeCur.BaseType;
}
}
}
return dynamicDependencies;
}
private void CreateDependencyForMethodSlotAndInstantiation(MethodDesc methodDef, List<CombinedDependencyListEntry> dynamicDependencies, NodeFactory factory)
{
Debug.Assert(methodDef != null);
Debug.Assert(!methodDef.Signature.IsStatic);
if (methodDef.IsAbstract)
return;
MethodDesc derivedMethodInstantiation = _method.Context.GetInstantiatedMethod(methodDef, _method.Instantiation);
// Universal canonical instantiations should be entirely universal canon
if (derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Universal))
{
derivedMethodInstantiation = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Universal);
}
// TODO: verify for invalid instantiations, like List<void>?
bool validInstantiation =
derivedMethodInstantiation.IsSharedByGenericInstantiations || // Non-exact methods are always valid instantiations (always pass constraints check)
derivedMethodInstantiation.CheckConstraints(); // Verify that the instantiation does not violate constraints
if (validInstantiation)
{
MethodDesc canonMethodTarget = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific);
bool getUnboxingStub = (derivedMethodInstantiation.OwningType.IsValueType || derivedMethodInstantiation.OwningType.IsEnum);
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(canonMethodTarget, getUnboxingStub), null, "DerivedMethodInstantiation"));
if (canonMethodTarget != derivedMethodInstantiation)
{
// Dependency includes the generic method dictionary of the instantiation, and all its dependencies. This is done by adding the
// ShadowConcreteMethod to the list of dynamic dependencies. The generic dictionary will be reported as a dependency of the ShadowConcreteMethod
// TODO: detect large recursive generics and fallback to USG templates
Debug.Assert(!derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Any));
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.ShadowConcreteMethod(derivedMethodInstantiation), null, "DerivedMethodInstantiation dictionary"));
}
}
else
{
// TODO: universal generics
}
}
}
}

Просмотреть файл

@ -77,7 +77,36 @@ namespace ILCompiler.DependencyAnalysis
if(!type.IsDefType || type.IsInterface)
return false;
return type.HasGenericVirtualMethod();
// Type declares GVMs
if (type.HasGenericVirtualMethods())
return true;
//
// Check if the type implements any interface with GVM methods, where the method implementations could be on
// base types.
// Example:
// interface IFace {
// void IFaceGVMethod<U>();
// }
// class BaseClass {
// public virtual void IFaceGVMethod<U>() { ... }
// }
// public class DerivedClass : BaseClass, IFace { }
//
foreach (var iface in type.RuntimeInterfaces)
{
foreach (var method in iface.GetMethods())
{
if (!method.HasInstantiation || method.Signature.IsStatic)
continue;
MethodDesc slotDecl = type.ResolveInterfaceMethodTarget(method);
if (slotDecl != null)
return true;
}
}
return false;
}
public IEnumerable<TypeGVMEntryInfo> ScanForGenericVirtualMethodEntries()

Просмотреть файл

@ -97,7 +97,7 @@ namespace ILCompiler
/// <summary>
/// Gets a value indicating whether this type has any generic virtual methods.
/// </summary>
public static bool HasGenericVirtualMethod(this TypeDesc type)
public static bool HasGenericVirtualMethods(this TypeDesc type)
{
foreach (var method in type.GetAllMethods())
{

Просмотреть файл

@ -259,6 +259,27 @@ namespace TypeSystemTests
instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object));
Assert.True(instantiatedType.CheckConstraints());
}
// InvalidInstantiationArgs
{
var pointer = _context.GetWellKnownType(WellKnownType.Int16).MakePointerType();
var byref = _context.GetWellKnownType(WellKnownType.Int16).MakeByRefType();
Assert.False(_iGenType.Instantiation.CheckValidInstantiationArguments());
instantiatedType = _iGenType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Void));
Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments());
instantiatedType = _iGenType.MakeInstantiatedType(pointer);
Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments());
instantiatedType = _iGenType.MakeInstantiatedType(byref);
Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments());
instantiatedType = _iGenType.MakeInstantiatedType(byref);
instantiatedType = _iGenType.MakeInstantiatedType(instantiatedType);
Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments());
}
}
}
}

Просмотреть файл

@ -21,12 +21,50 @@ namespace Internal.Runtime.TypeLoader
{
public sealed partial class TypeLoaderEnvironment
{
#if GVM_RESOLUTION_TRACE
private string GetTypeNameDebug(RuntimeTypeHandle rtth)
{
string result;
if (RuntimeAugments.IsGenericType(rtth))
{
RuntimeTypeHandle[] typeArgumentsHandles;
RuntimeTypeHandle openTypeDef = RuntimeAugments.GetGenericInstantiation(rtth, out typeArgumentsHandles); ;
result = GetTypeNameDebug(openTypeDef) + "<";
for (int i = 0; i < typeArgumentsHandles.Length; i++)
result += (i == 0 ? "" : ",") + GetTypeNameDebug(typeArgumentsHandles[i]);
return result + ">";
}
else
{
System.Reflection.Runtime.General.QTypeDefinition qTypeDefinition;
// Check if we have metadata.
if (Instance.TryGetMetadataForNamedType(rtth, out qTypeDefinition))
return qTypeDefinition.NativeFormatHandle.GetFullName(qTypeDefinition.NativeFormatReader);
}
result = "EEType:0x";
ulong num = (ulong)RuntimeAugments.GetPointerFromTypeHandle(rtth);
int shift = IntPtr.Size * 8;
const string HexDigits = "0123456789ABCDEF";
while (shift > 0)
{
shift -= 4;
int digit = (int)((num >> shift) & 0xF);
result += HexDigits[digit];
}
return result;
}
#endif
public bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated)
{
MethodNameAndSignature methodNameAndSignature = new MethodNameAndSignature(methodName, methodSignature);
#if REFLECTION_EXECUTION_TRACE
ReflectionExecutionLogger.WriteLine("GVM resolution starting for " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name + "(...) on a target of type " + GetTypeNameDebug(targetHandle) + " ...");
#if GVM_RESOLUTION_TRACE
Debug.WriteLine("GVM resolution starting for " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name + "(...) on a target of type " + GetTypeNameDebug(targetHandle) + " ...");
#endif
if (RuntimeAugments.IsInterface(declaringType))
@ -83,14 +121,19 @@ namespace Internal.Runtime.TypeLoader
{
uint numTargetImplementations = entryParser.GetUnsigned();
#if GVM_RESOLUTION_TRACE
Debug.WriteLine(" :: Declaring type = " + GetTypeNameDebug(declaringType));
Debug.WriteLine(" :: Target type = " + GetTypeNameDebug(openTargetTypeHandle));
#endif
for (uint j = 0; j < numTargetImplementations; j++)
{
uint nameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned());
MethodNameAndSignature targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, nameAndSigToken);
RuntimeTypeHandle targetTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
#if REFLECTION_EXECUTION_TRACE
ReflectionExecutionLogger.WriteLine(" Searching for GVM implementation on targe type = " + GetTypeNameDebug(targetTypeHandle));
#if GVM_RESOLUTION_TRACE
Debug.WriteLine(" Searching for GVM implementation on targe type = " + GetTypeNameDebug(targetTypeHandle));
#endif
uint numIfaceImpls = entryParser.GetUnsigned();
@ -99,6 +142,10 @@ namespace Internal.Runtime.TypeLoader
{
RuntimeTypeHandle implementingTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
#if GVM_RESOLUTION_TRACE
Debug.WriteLine(" -> Current implementing type = " + GetTypeNameDebug(implementingTypeHandle));
#endif
uint numIfaceSigs = entryParser.GetUnsigned();
if (!openTargetTypeHandle.Equals(implementingTypeHandle))
@ -118,13 +165,16 @@ namespace Internal.Runtime.TypeLoader
if (TypeLoaderEnvironment.Instance.GetTypeFromSignatureAndContext(ref ifaceSigParser, module.Handle, targetTypeInstantiation, null, out currentIfaceTypeHandle))
{
#if GVM_RESOLUTION_TRACE
Debug.WriteLine(" -> Current interface on type = " + GetTypeNameDebug(currentIfaceTypeHandle));
#endif
Debug.Assert(!currentIfaceTypeHandle.IsNull());
if ((!variantDispatch && declaringType.Equals(currentIfaceTypeHandle)) ||
(variantDispatch && RuntimeAugments.IsAssignableFrom(declaringType, currentIfaceTypeHandle)))
{
#if REFLECTION_EXECUTION_TRACE
ReflectionExecutionLogger.WriteLine(" " + (declaringType.Equals(currentIfaceTypeHandle) ? "Exact" : "Variant-compatible") + " match found on this target type!");
#if GVM_RESOLUTION_TRACE
Debug.WriteLine(" " + (declaringType.Equals(currentIfaceTypeHandle) ? "Exact" : "Variant-compatible") + " match found on this target type!");
#endif
// We found the GVM slot target for the input interface GVM call, so let's update the interface GVM slot and return success to the caller
declaringType = targetTypeHandle;
@ -197,8 +247,8 @@ namespace Internal.Runtime.TypeLoader
RuntimeTypeHandle[] targetTypeInstantiation;
openTargetTypeHandle = GetOpenTypeDefinition(targetTypeHandle, out targetTypeInstantiation);
#if REFLECTION_EXECUTION_TRACE
ReflectionExecutionLogger.WriteLine("INTERFACE GVM call = " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name);
#if GVM_RESOLUTION_TRACE
Debug.WriteLine("INTERFACE GVM call = " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name);
#endif
foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle)))
@ -427,8 +477,8 @@ namespace Internal.Runtime.TypeLoader
int hashCode = openCallingTypeHandle.GetHashCode();
hashCode = ((hashCode << 13) ^ hashCode) ^ openTargetTypeHandle.GetHashCode();
#if REFLECTION_EXECUTION_TRACE
ReflectionExecutionLogger.WriteLine("GVM Target Resolution = " + GetTypeNameDebug(targetTypeHandle) + "." + callingMethodNameAndSignature.Name);
#if GVM_RESOLUTION_TRACE
Debug.WriteLine("GVM Target Resolution = " + GetTypeNameDebug(targetTypeHandle) + "." + callingMethodNameAndSignature.Name);
#endif
foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle)))

Просмотреть файл

@ -144,13 +144,6 @@
<ExcludeList Include="$(XunitTestBinBase)\JIT\jit64\opt\cse\HugeArray1\HugeArray1.*" />
<ExcludeList Include="$(XunitTestBinBase)\JIT\jit64\opt\cse\hugeexpr1\hugeexpr1.*" />
<!-- Generic virtual methods -->
<!-- https://github.com/dotnet/corert/issues/3454 -->
<ExcludeList Include="$(XunitTestBinBase)\JIT\Regression\CLR-x86-JIT\V1.2-Beta1\b210352\csharptester\csharptester.*" />
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\generics\Variance\Regressions\dev10_468712\dev10_468712\dev10_468712.*" />
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\methodoverriding\regressions\dev10_432038\dev10_432038\dev10_432038.*" />
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\regressions\dev10_398410\dev10_398410\dev10_398410.*" />
<!-- Reflection enabling a virtual method methodimpl'd by other virtual method -->
<ExcludeList Include="$(XunitTestBinBase)\Loader\classloader\methodoverriding\regressions\576621\VSW576621\VSW576621.*" />

Просмотреть файл

@ -1142,6 +1142,21 @@ class Program
string IMethod1<T>(T t1, T t2);
}
interface ICovariant<out T>
{
string ICovariantGVM<U>();
}
public interface IBar<T>
{
U IBarGVMethod<U>(Func<T, U> arg);
}
public interface IFace<T>
{
string IFaceGVMethod1<U>(T t, U u);
}
class Base : IFoo<string>, IFoo<int>
{
public virtual string GMethod1<T>(T t1, T t2) { return "Base.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; }
@ -1189,6 +1204,36 @@ class Program
public string IMethod1<T>(T t1, T t2) { return "MyStruct3.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; }
}
public class AnotherBaseClass<T>
{
public virtual string IFaceMethod1(T t) { return "AnotherBaseClass.IFaceMethod1"; }
public virtual string IFaceGVMethod1<U>(T t, U u) { return "AnotherBaseClass.IFaceGVMethod1"; }
}
public class AnotherDerivedClass<T> : AnotherBaseClass<T>, IFace<T>
{
}
public class BarImplementor : IBar<int>
{
public virtual U IBarGVMethod<U>(Func<int, U> arg) { return arg(123); }
}
public class Yahoo<T>
{
public virtual U YahooGVM<U>(Func<T, U> arg) { return default(U); }
}
public class YahooDerived : Yahoo<int>
{
public override U YahooGVM<U>(Func<int, U> arg) { return arg(456); }
}
public class Covariant<T> : ICovariant<T>
{
public string ICovariantGVM<U>() { return String.Format("Covariant<{0}>.ICovariantGVM<{1}>", typeof(T).Name, typeof(U).Name); }
}
static string s_GMethod1;
static string s_IFooString;
static string s_IFooObject;
@ -1342,6 +1387,20 @@ class Program
Console.WriteLine("====================");
}
{
string res = ((IFace<string>)new AnotherDerivedClass<string>()).IFaceGVMethod1<string>("string1", "string2");
WriteLineWithVerification("AnotherBaseClass.IFaceGVMethod1", res);
res = ((IBar<int>)new BarImplementor()).IBarGVMethod<string>((i) => "BarImplementor:" + i.ToString());
WriteLineWithVerification("BarImplementor:123", res);
Yahoo<int> y = new YahooDerived();
WriteLineWithVerification("YahooDerived:456", y.YahooGVM<string>((i) => "YahooDerived:" + i.ToString()));
ICovariant<object> cov = new Covariant<string>();
WriteLineWithVerification("Covariant<String>.ICovariantGVM<Exception>", cov.ICovariantGVM<Exception>());
}
if (s_NumErrors != 0)
throw new Exception();
}