Skip to content

Commit

Permalink
Share Type.Base intrinsic (#2681)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-karas authored Mar 11, 2022
1 parent 93eac59 commit 98c81d5
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 59 deletions.
11 changes: 11 additions & 0 deletions src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy typ
yield return new SystemTypeValue (new TypeProxy (nestedType));
}

private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType)
{
if (type.Type.BaseType is not null) {
baseType = new TypeProxy (type.Type.BaseType);
return true;
}

baseType = null;
return false;
}

// TODO: Does the analyzer need to do something here?
private partial void MarkStaticConstructor (TypeProxy type) { }

Expand Down
51 changes: 51 additions & 0 deletions src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,55 @@ public bool Invoke (MethodProxy calledMethod, MultiValue instanceValue, IReadOnl
}
break;

//
// Type.BaseType
//
case IntrinsicId.Type_get_BaseType: {
foreach (var value in instanceValue) {
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) {
DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
propagatedMemberTypes = DynamicallyAccessedMemberTypes.All;
else {
// PublicConstructors are not propagated to base type

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods;

// PublicNestedTypes are not propagated to base type

// PublicParameterlessConstructor is not propagated to base type

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.Interfaces))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.Interfaces;
}

AddReturnValue (GetMethodReturnValue (calledMethod, propagatedMemberTypes));
} else if (value is SystemTypeValue systemTypeValue) {
if (TryGetBaseType (systemTypeValue.RepresentedType, out var baseType))
AddReturnValue (new SystemTypeValue (baseType.Value));
else
AddReturnValue (GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes));
} else if (value == NullValue.Instance) {
// Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis
continue;
} else {
// Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it
AddReturnValue (GetMethodReturnValue (calledMethod, returnValueDynamicallyAccessedMemberTypes));
}
}
}
break;

case IntrinsicId.None:
methodReturnValue = MultiValueLattice.Top;
return false;
Expand Down Expand Up @@ -686,6 +735,8 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes

private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags);

private partial bool TryGetBaseType (TypeProxy type, [NotNullWhen (true)] out TypeProxy? baseType);

private partial void MarkStaticConstructor (TypeProxy type);

private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags);
Expand Down
11 changes: 11 additions & 0 deletions src/linker/Linker.Dataflow/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy typ
yield return new SystemTypeValue (new TypeProxy (nestedType));
}

private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType)
{
if (type.Type.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) {
baseType = new TypeProxy (baseTypeDefinition);
return true;
}

baseType = null;
return false;
}

private partial void MarkStaticConstructor (TypeProxy type)
=> _reflectionMethodBodyScanner.MarkStaticConstructor (_analysisContext, type.Type);

Expand Down
52 changes: 2 additions & 50 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
case IntrinsicId.Type_GetMethod:
case IntrinsicId.Type_GetNestedType:
case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection.MethodInfo"):
case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: {
case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property:
case IntrinsicId.Type_get_BaseType: {
var instanceValue = MultiValueLattice.Top;
IReadOnlyList<MultiValue> parameterValues = methodParams;
if (calledMethodDefinition.HasImplicitThis ()) {
Expand Down Expand Up @@ -576,55 +577,6 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
}
break;

//
// Type.BaseType
//
case IntrinsicId.Type_get_BaseType: {
foreach (var value in methodParams[0]) {
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) {
DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
propagatedMemberTypes = DynamicallyAccessedMemberTypes.All;
else {
// PublicConstructors are not propagated to base type

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods;

// PublicNestedTypes are not propagated to base type

// PublicParameterlessConstructor is not propagated to base type

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties;

if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.Interfaces))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.Interfaces;
}

methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition, propagatedMemberTypes));
} else if (value is SystemTypeValue systemTypeValue) {
if (systemTypeValue.RepresentedType.Type.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition)
methodReturnValue = MultiValueLattice.Meet (methodReturnValue, new SystemTypeValue (baseTypeDefinition));
else
methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition));
} else if (value == NullValue.Instance) {
// Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis
continue;
} else {
// Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it
methodReturnValue = MultiValueLattice.Meet (methodReturnValue, GetMethodReturnValue (calledMethodDefinition));
}
}
}
break;

//
// System.Activator
//
Expand Down
3 changes: 1 addition & 2 deletions test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,7 @@ public Task SuppressWarningWithLinkAttributes ()
[Fact]
public Task TypeBaseTypeDataFlow ()
{
// /~https://github.com/dotnet/linker/issues/2273
return RunTest (allowMissingWarnings: true);
return RunTest ();
}

[Fact]
Expand Down
6 changes: 6 additions & 0 deletions test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ public Task RuntimeReflectionExtensionsCalls ()
return RunTest ();
}

[Fact]
public Task TypeBaseTypeUseViaReflection ()
{
return RunTest ();
}

[Fact]
public Task TypeHierarchyReflectionWarnings ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ public Task RunClassConstructorUsedViaReflection ()
return RunTest (allowMissingWarnings: true);
}

[Fact]
public Task TypeBaseTypeUseViaReflection ()
{
return RunTest (allowMissingWarnings: true);
}

[Fact]
public Task TypeDelegator ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ static void TestAllPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedM
derivedType.BaseType.RequiresAll ();
}

[ExpectedWarning ("IL2062", nameof (TestAllPropagated))]
// This is a very special case - normally there's basically no way to "new up" a Type instance via the "new" operator
// so the analyzer doesn't handle this case at all - meaning it sees it as empty value.
// Unlike linker which sees an unknown value and thus warns that it doesn't fulfill the All annotation.
// It's OK for the analyzer to be more forgiving in this case, due to the very special circumstances.
[ExpectedWarning ("IL2062", nameof (TestAllPropagated), ProducedBy = ProducedBy.Trimmer)]
public static void Test ()
{
TestAllPropagated (new TestSystemTypeBase ());
Expand Down

0 comments on commit 98c81d5

Please sign in to comment.