Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for static virtual methods #66084

Merged
merged 1 commit into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions src/coreclr/tools/Common/Compiler/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,11 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai
{
forceRuntimeLookup = false;

bool isStaticVirtualMethod = interfaceMethod.Signature.IsStatic;

// We can't resolve constraint calls effectively for reference types, and there's
// not a lot of perf. benefit in doing it anyway.
if (!constrainedType.IsValueType)
if (!constrainedType.IsValueType && (!isStaticVirtualMethod || constrainedType.IsCanonicalDefinitionType(CanonicalFormKind.Any)))
{
return null;
}
Expand Down Expand Up @@ -466,10 +468,17 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai
potentialInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)potentialInterfaceType);
}

method = canonType.ResolveInterfaceMethodToVirtualMethodOnType(potentialInterfaceMethod);
if (isStaticVirtualMethod)
{
method = canonType.ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(potentialInterfaceMethod);
}
else
{
method = canonType.ResolveInterfaceMethodToVirtualMethodOnType(potentialInterfaceMethod);
}

// See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod
if (method != null && !method.OwningType.IsValueType)
if (!isStaticVirtualMethod && method != null && !method.OwningType.IsValueType)
{
// We explicitly wouldn't want to abort if we found a default implementation.
// The above resolution doesn't consider the default methods.
Expand Down Expand Up @@ -500,7 +509,14 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai
// We can resolve to exact method
MethodDesc exactInterfaceMethod = context.GetMethodForInstantiatedType(
genInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceType);
method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod);
if (isStaticVirtualMethod)
{
method = constrainedType.ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(exactInterfaceMethod);
}
else
{
method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod);
}
isExactMethodResolved = method != null;
}
}
Expand All @@ -523,7 +539,14 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai
if (genInterfaceMethod.OwningType != interfaceType)
exactInterfaceMethod = context.GetMethodForInstantiatedType(
genInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceType);
method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod);
if (isStaticVirtualMethod)
{
method = constrainedType.ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(exactInterfaceMethod);
}
else
{
method = constrainedType.ResolveVariantInterfaceMethodToVirtualMethodOnType(exactInterfaceMethod);
}
}
}
}
Expand All @@ -548,7 +571,7 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai
//#TryResolveConstraintMethodApprox_DoNotReturnParentMethod
// Only return a method if the value type itself declares the method,
// otherwise we might get a method from Object or System.ValueType
if (!method.OwningType.IsValueType)
if (!isStaticVirtualMethod && !method.OwningType.IsValueType)
{
// Fall back to VSD
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,16 @@ public override MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(Me
return ResolveVariantInterfaceMethodToVirtualMethodOnType(interfaceMethod, (MetadataType)currentType);
}

public override MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType)
{
return ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, (MetadataType)currentType);
}

public override MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType)
{
return ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, (MetadataType)currentType);
}

//////////////////////// INTERFACE RESOLUTION
//Interface function resolution
// Interface function resolution follows the following rules
Expand All @@ -588,6 +598,8 @@ public override MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(Me
// See current interface call resolution for details on how that happens.
private static MethodDesc ResolveInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType)
{
Debug.Assert(!interfaceMethod.Signature.IsStatic);

if (currentType.IsInterface)
return null;

Expand Down Expand Up @@ -657,6 +669,8 @@ private static MethodDesc ResolveInterfaceMethodToVirtualMethodOnType(MethodDesc

public static MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType)
{
Debug.Assert(!interfaceMethod.Signature.IsStatic);

MetadataType interfaceType = (MetadataType)interfaceMethod.OwningType;
bool foundInterface = IsInterfaceImplementedOnType(currentType, interfaceType);
MethodDesc implMethod;
Expand Down Expand Up @@ -841,5 +855,108 @@ public static IEnumerable<MethodDesc> EnumAllVirtualSlots(MetadataType type)
} while (type != null);
}
}

/// <summary>
/// Try to resolve a given virtual static interface method on a given constrained type and its base types.
/// </summary>
/// <param name="interfaceMethod">Interface method to resolve</param>
/// <param name="currentType">Type to attempt virtual static method resolution on</param>
/// <returns>MethodDesc of the resolved virtual static method, null when not found (runtime lookup must be used)</returns>
public static MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType)
{
TypeDesc interfaceType = interfaceMethod.OwningType;

// Search for match on a per-level in the type hierarchy
for (MetadataType typeToCheck = currentType; typeToCheck != null; typeToCheck = typeToCheck.MetadataBaseType)
{
MethodDesc resolvedMethodOnType = TryResolveVirtualStaticMethodOnThisType(typeToCheck, interfaceMethod);
if (resolvedMethodOnType != null)
{
return resolvedMethodOnType;
}
}
return null;
}

/// <summary>
/// Try to resolve a given virtual static interface method on a given constrained type and its base types.
/// </summary>
/// <param name="interfaceMethod">Interface method to resolve</param>
/// <param name="currentType">Type to attempt virtual static method resolution on</param>
/// <returns>MethodDesc of the resolved virtual static method, null when not found (runtime lookup must be used)</returns>
public static MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType)
{
TypeDesc interfaceType = interfaceMethod.OwningType;

// Search for match on a per-level in the type hierarchy
for (MetadataType typeToCheck = currentType; typeToCheck != null; typeToCheck = typeToCheck.MetadataBaseType)
{
MethodDesc resolvedMethodOnType = TryResolveVirtualStaticMethodOnThisType(typeToCheck, interfaceMethod);
if (resolvedMethodOnType != null)
{
return resolvedMethodOnType;
}

// Variant interface dispatch
foreach (DefType runtimeInterfaceType in typeToCheck.RuntimeInterfaces)
{
if (runtimeInterfaceType == interfaceType)
{
// This is the variant interface check logic, skip this
continue;
}

if (!runtimeInterfaceType.HasSameTypeDefinition(interfaceType))
{
// Variance matches require a typedef match
// Equivalence isn't sufficient, and is uninteresting as equivalent interfaces cannot have static virtuals.
continue;
}

if (runtimeInterfaceType.CanCastTo(interfaceType))
{
// Attempt to resolve on variance matched interface
MethodDesc runtimeInterfaceMethod = runtimeInterfaceType.FindMethodOnExactTypeWithMatchingTypicalMethod(interfaceMethod);
resolvedMethodOnType = TryResolveVirtualStaticMethodOnThisType(typeToCheck, runtimeInterfaceMethod);
if (resolvedMethodOnType != null)
{
return resolvedMethodOnType;
}
}
}
}
return null;
}

/// <summary>
/// Try to resolve a given virtual static interface method on a given constrained type and return the resolved method or null when not found.
/// </summary>
/// <param name="constrainedType">Type to attempt method resolution on</param>
/// <param name="interfaceMethod">Method to resolve</param>
/// <returns>MethodDesc of the resolved method or null when not found (runtime lookup must be used)</returns>
private static MethodDesc TryResolveVirtualStaticMethodOnThisType(MetadataType constrainedType, MethodDesc interfaceMethod)
{
Debug.Assert(interfaceMethod.Signature.IsStatic);

MethodImplRecord[] possibleImpls = constrainedType.FindMethodsImplWithMatchingDeclName(interfaceMethod.Name);
if (possibleImpls == null)
return null;

MethodDesc interfaceMethodDefinition = interfaceMethod.GetMethodDefinition();
foreach (MethodImplRecord methodImpl in possibleImpls)
{
if (methodImpl.Decl == interfaceMethodDefinition)
{
MethodDesc resolvedMethodImpl = methodImpl.Body;
if (interfaceMethod != interfaceMethodDefinition)
{
resolvedMethodImpl = resolvedMethodImpl.MakeInstantiatedMethod(interfaceMethod.Instantiation);
}
return resolvedMethodImpl;
}
}

return null;
}
}
}
10 changes: 10 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/TypeSystemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@ public static MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(this
return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveVariantInterfaceMethodToVirtualMethodOnType(interfaceMethod, type);
}

public static MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType(this TypeDesc type, MethodDesc interfaceMethod)
{
return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, type);
}

public static MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(this TypeDesc type, MethodDesc interfaceMethod)
{
return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod, type);
}

public static DefaultInterfaceMethodResolution ResolveInterfaceMethodToDefaultImplementationOnType(this TypeDesc type, MethodDesc interfaceMethod, out MethodDesc implMethod)
{
return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, type, out implMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public abstract class VirtualMethodAlgorithm

public abstract MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType);

public abstract MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType);

public abstract MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType);

public abstract DefaultInterfaceMethodResolution ResolveInterfaceMethodToDefaultImplementationOnType(MethodDesc interfaceMethod, TypeDesc currentType, out MethodDesc impl);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo
case ReadyToRunHelperId.FieldHandle:
return ((FieldDesc)targetOfLookup).OwningType.IsRuntimeDeterminedSubtype;

case ReadyToRunHelperId.ConstrainedDirectCall:
return ((ConstrainedCallInfo)targetOfLookup).Method.IsRuntimeDeterminedExactMethod
|| ((ConstrainedCallInfo)targetOfLookup).ConstrainedType.IsRuntimeDeterminedSubtype;

default:
throw new NotImplementedException();
}
Expand Down Expand Up @@ -643,4 +647,12 @@ public IEnumerable<TypeDesc> ConstructedEETypes
}
}
}

public sealed class ConstrainedCallInfo
{
public readonly TypeDesc ConstrainedType;
public readonly MethodDesc Method;
public ConstrainedCallInfo(TypeDesc constrainedType, MethodDesc method)
=> (ConstrainedType, Method) = (constrainedType, method);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ public override bool InterestingForDynamicDependencyAnalysis
{
Debug.Assert(method.IsVirtual);

// Static interface methods don't participate in GVM analysis
if (method.Signature.IsStatic)
continue;

if (method.HasInstantiation)
{
// We found a GVM on one of the implemented interfaces. Find if the type implements this method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1431,20 +1431,36 @@ public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultCo
{
MethodDesc instantiatedConstrainedMethod = _constrainedMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation);
TypeDesc instantiatedConstraintType = _constraintType.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation);
MethodDesc implMethod = instantiatedConstrainedMethod;
MethodDesc implMethod;

if (implMethod.OwningType.IsInterface)
if (instantiatedConstrainedMethod.OwningType.IsInterface)
{
implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToVirtualMethodOnType(implMethod);
if (instantiatedConstrainedMethod.Signature.IsStatic)
{
implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(instantiatedConstrainedMethod);
}
else
{
throw new NotImplementedException();
}
}
else
{
implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(instantiatedConstrainedMethod);
}

implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(implMethod);

// AOT use of this generic lookup is restricted to finding methods on valuetypes (runtime usage of this slot in universal generics is more flexible)
Debug.Assert(instantiatedConstraintType.IsValueType);
Debug.Assert(implMethod.OwningType == instantiatedConstraintType);
Debug.Assert(instantiatedConstraintType.IsValueType || (instantiatedConstrainedMethod.OwningType.IsInterface && instantiatedConstrainedMethod.Signature.IsStatic));
Debug.Assert(!instantiatedConstraintType.IsValueType || implMethod.OwningType == instantiatedConstraintType);

if (implMethod.HasInstantiation)
if (implMethod.Signature.IsStatic)
{
if (implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).IsSharedByGenericInstantiations)
return factory.ExactCallableAddress(implMethod);
else
return factory.MethodEntrypoint(implMethod);
}
else if (implMethod.HasInstantiation)
{
return factory.ExactCallableAddress(implMethod);
}
Expand All @@ -1467,7 +1483,8 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde

public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory)
{
return factory.NativeLayout.ConstrainedMethodUse(_constrainedMethod, _constraintType, _directCall);
return factory.NativeLayout.NotSupportedDictionarySlot;
//return factory.NativeLayout.ConstrainedMethodUse(_constrainedMethod, _constraintType, _directCall);
}

public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public static bool MightHaveInterfaceDispatchMap(TypeDesc type, NodeFactory fact

foreach (MethodDesc slotMethod in slots)
{
// Static interface methods don't go in the dispatch map
if (slotMethod.Signature.IsStatic)
continue;

MethodDesc declMethod = slotMethod;

Debug.Assert(!declMethod.Signature.IsStatic && declMethod.IsVirtual);
Expand Down
Loading