Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -972,25 +972,26 @@ public override IEnumerable<MethodDesc> ComputeAllVirtualSlots(TypeDesc type)

// Enumerate all possible virtual slots of a type
public static IEnumerable<MethodDesc> EnumAllVirtualSlots(MetadataType type)
{
return type.IsInterface ? type.GetAllVirtualMethods() : EnumAllVirtualSlotsOnClass(type);
}
private static IEnumerable<MethodDesc> EnumAllVirtualSlotsOnClass(MetadataType type)
{
MethodDescHashtable alreadyEnumerated = new MethodDescHashtable();
if (!type.IsInterface)
do
{
do
foreach (MethodDesc m in type.GetAllVirtualMethods())
{
foreach (MethodDesc m in type.GetAllVirtualMethods())
MethodDesc possibleVirtual = FindSlotDefiningMethodForVirtualMethod(m);
if (!alreadyEnumerated.Contains(possibleVirtual))
{
MethodDesc possibleVirtual = FindSlotDefiningMethodForVirtualMethod(m);
if (!alreadyEnumerated.Contains(possibleVirtual))
{
alreadyEnumerated.AddOrGetExisting(possibleVirtual);
yield return possibleVirtual;
}
alreadyEnumerated.AddOrGetExisting(possibleVirtual);
yield return possibleVirtual;
}
}

type = type.BaseType;
} while (type != null);
}
type = type.BaseType;
} while (type != null);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private static VirtualMethodAnalysisFlags AnalyzeVirtualMethods(TypeDesc type)
//
foreach (DefType interfaceImpl in defType.RuntimeInterfaces)
{
foreach (MethodDesc method in interfaceImpl.GetAllVirtualMethods())
foreach (MethodDesc method in interfaceImpl.EnumAllVirtualSlots())
{
if (!method.HasInstantiation)
continue;
Expand Down Expand Up @@ -369,56 +369,59 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep
// If we're producing a full vtable, none of the dependencies are conditional.
if (!factory.VTable(defType).HasKnownVirtualMethodUse)
{
bool isNonInterfaceAbstractType = !defType.IsInterface && ((MetadataType)defType).IsAbstract;

foreach (MethodDesc decl in defType.EnumAllVirtualSlots())
if (!defType.IsInterface)
{
// Generic virtual methods are tracked by an orthogonal mechanism.
if (decl.HasInstantiation)
continue;
bool isAbstractType = ((MetadataType)defType).IsAbstract;

MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl);
bool implOwnerIsAbstract = ((MetadataType)impl.OwningType).IsAbstract;

// We add a conditional dependency in two situations:
// 1. The implementation is on this type. This is pretty obvious.
// 2. The implementation comes from an abstract base type. We do this
// because abstract types only request a TentativeMethodEntrypoint of the implementation.
// The actual method body of this entrypoint might still be trimmed away.
// We don't need to do this for implementations from non-abstract bases since
// non-abstract types will create a hard conditional reference to their virtual
// method implementations.
//
// We also skip abstract methods since they don't have a body to refer to.
if ((impl.OwningType == defType || implOwnerIsAbstract) && !impl.IsAbstract)
foreach (MethodDesc decl in defType.EnumAllVirtualSlots())
{
MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific);
// Generic virtual methods are tracked by an orthogonal mechanism.
if (decl.HasInstantiation)
continue;

// If this is an abstract type, only request a tentative entrypoint (whose body
// might just be stubbed out). This lets us avoid generating method bodies for
// virtual method on abstract types that are overridden in all their children.
MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl);
bool implOwnerIsAbstract = ((MetadataType)impl.OwningType).IsAbstract;

// We add a conditional dependency in two situations:
// 1. The implementation is on this type. This is pretty obvious.
// 2. The implementation comes from an abstract base type. We do this
// because abstract types only request a TentativeMethodEntrypoint of the implementation.
// The actual method body of this entrypoint might still be trimmed away.
// We don't need to do this for implementations from non-abstract bases since
// non-abstract types will create a hard conditional reference to their virtual
// method implementations.
//
// We don't do this if the method can be placed in the sealed vtable since
// those can never be overridden by children anyway.
bool canUseTentativeMethod = isNonInterfaceAbstractType
&& !decl.CanMethodBeInSealedVTable(factory)
&& factory.CompilationModuleGroup.AllowVirtualMethodOnAbstractTypeOptimization(canonImpl);
IMethodNode implNode = canUseTentativeMethod ?
factory.TentativeMethodEntrypoint(canonImpl, impl.OwningType.IsValueType) :
factory.MethodEntrypoint(canonImpl, impl.OwningType.IsValueType);
result.Add(new CombinedDependencyListEntry(implNode, factory.VirtualMethodUse(decl), "Virtual method"));
// We also skip abstract methods since they don't have a body to refer to.
if ((impl.OwningType == defType || implOwnerIsAbstract) && !impl.IsAbstract)
{
MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific);

// If this is an abstract type, only request a tentative entrypoint (whose body
// might just be stubbed out). This lets us avoid generating method bodies for
// virtual method on abstract types that are overridden in all their children.
//
// We don't do this if the method can be placed in the sealed vtable since
// those can never be overridden by children anyway.
bool canUseTentativeMethod = isAbstractType
&& !decl.CanMethodBeInSealedVTable(factory)
&& factory.CompilationModuleGroup.AllowVirtualMethodOnAbstractTypeOptimization(canonImpl);
IMethodNode implNode = canUseTentativeMethod ?
factory.TentativeMethodEntrypoint(canonImpl, impl.OwningType.IsValueType) :
factory.MethodEntrypoint(canonImpl, impl.OwningType.IsValueType);
result.Add(new CombinedDependencyListEntry(implNode, factory.VirtualMethodUse(decl), "Virtual method"));

result.Add(new CombinedDependencyListEntry(
factory.AddressTakenMethodEntrypoint(canonImpl, impl.OwningType.IsValueType),
factory.DelegateTargetVirtualMethod(decl.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Slot is a delegate target"));
}

result.Add(new CombinedDependencyListEntry(
factory.AddressTakenMethodEntrypoint(canonImpl, impl.OwningType.IsValueType),
factory.DelegateTargetVirtualMethod(decl.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Slot is a delegate target"));
}
if (impl.OwningType == defType)
{
factory.MetadataManager.NoteOverridingMethod(decl, impl);
}

if (impl.OwningType == defType)
{
factory.MetadataManager.NoteOverridingMethod(decl, impl);
factory.MetadataManager.GetDependenciesForOverridingMethod(ref result, factory, decl, impl);
}

factory.MetadataManager.GetDependenciesForOverridingMethod(ref result, factory, decl, impl);
}

Debug.Assert(
Expand Down Expand Up @@ -448,7 +451,7 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep

bool isVariantInterfaceImpl = VariantInterfaceMethodUseNode.IsVariantInterfaceImplementation(factory, _type, interfaceType);

foreach (MethodDesc interfaceMethod in interfaceType.GetAllVirtualMethods())
foreach (MethodDesc interfaceMethod in interfaceType.EnumAllVirtualSlots())
{
// Generic virtual methods are tracked by an orthogonal mechanism.
if (interfaceMethod.HasInstantiation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1055,30 +1055,6 @@ public override Vertex WriteVertex(NodeFactory factory)

return SetSavedVertex(layoutInfo);
}

private static IEnumerable<MethodDesc> EnumVirtualSlotsDeclaredOnType(TypeDesc declType)
{
// VirtualMethodUse of Foo<SomeType>.Method will bring in VirtualMethodUse
// of Foo<__Canon>.Method. This in turn should bring in Foo<OtherType>.Method.
DefType defType = declType.GetClosestDefType();

Debug.Assert(!declType.IsInterface);

IEnumerable<MethodDesc> allSlots = defType.EnumAllVirtualSlots();

foreach (var method in allSlots)
{
// Generic virtual methods are tracked by an orthogonal mechanism.
if (method.HasInstantiation)
continue;

// Current type doesn't define this slot. Another VTableSlice will take care of this.
if (method.OwningType != defType)
continue;

yield return method;
}
}
}

public abstract class NativeLayoutGenericDictionarySlotNode : NativeLayoutVertexNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public InterfaceGVMEntryInfo(MethodDesc callingMethod, MethodDesc implementation
public TypeGVMEntriesNode(TypeDesc associatedType)
{
Debug.Assert(associatedType.IsTypeDefinition);
Debug.Assert(!associatedType.IsInterface);
_associatedType = associatedType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ protected static MethodDesc[] ComputeSlots(TypeDesc type)
bool isObjectType = type.IsObject;
DefType defType = type.GetClosestDefType();

IEnumerable<MethodDesc> allSlots = type.IsInterface ?
type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots();
IEnumerable<MethodDesc> allSlots = defType.EnumAllVirtualSlots();

foreach (var method in allSlots)
{
Expand Down Expand Up @@ -224,8 +223,7 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep
// of Foo<__Canon>.Method. This in turn should bring in Foo<OtherType>.Method.
DefType defType = _type.GetClosestDefType();

IEnumerable<MethodDesc> allSlots = _type.IsInterface ?
_type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots();
IEnumerable<MethodDesc> allSlots = defType.EnumAllVirtualSlots();

foreach (var method in allSlots)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,40 +494,43 @@ Task<bool> ValidateTypeWorkerHelper(TypeDesc typeToCheckForSkipValidation)
}
}

foreach (var virtualMethod in type.EnumAllVirtualSlots())
if (!type.IsInterface)
{
var implementationMethod = virtualMethodAlgorithm.FindVirtualFunctionTargetMethodOnObjectType(virtualMethod, type);

if (implementationMethod != null)
foreach (var virtualMethod in type.EnumAllVirtualSlots())
{
// 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;
}
var implementationMethod = virtualMethodAlgorithm.FindVirtualFunctionTargetMethodOnObjectType(virtualMethod, type);

// 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))
if (implementationMethod != null)
{
var implementationOnBaseType = baseTypeVirtualMethodAlgorithm.FindVirtualFunctionTargetMethodOnObjectType(virtualMethod, type.BaseType);
if (!implementationMethod.Signature.ApplySubstitution(type.Instantiation).EquivalentWithCovariantReturnType(implementationOnBaseType.Signature.ApplySubstitution(type.Instantiation)))
// 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}' does not satisfy the covariant return type introduced with '{implementationOnBaseType}'");
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)
// 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)
{
AddTypeValidationError(type, $"Interface method '{virtualMethod}' does not have implementation");
return false;
if (implementationMethod == null || implementationMethod.IsAbstract)
{
AddTypeValidationError(type, $"Interface method '{virtualMethod}' does not have implementation");
return false;
}
}
}
}
Expand Down
Loading