diff options
author | Vitek Karas <10670590+vitek-karas@users.noreply.github.com> | 2022-03-12 00:33:31 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-12 00:33:31 +0300 |
commit | 98c81d59e153a5bf4329470b9d0fbf6d58fdd6e8 (patch) | |
tree | 15d2a8541b0da2caf115d0b73dc66a8918efaa34 | |
parent | 93eac59827cae579c0889e293b715cec93b48c62 (diff) |
Share Type.Base intrinsic (#2681)
8 files changed, 87 insertions, 59 deletions
diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 6e743f9b7..f05a5d3e1 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -62,6 +62,17 @@ namespace ILLink.Shared.TrimAnalysis 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) { } diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 002c213b9..c20e3b6f0 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -540,6 +540,55 @@ namespace ILLink.Shared.TrimAnalysis } 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; @@ -686,6 +735,8 @@ namespace ILLink.Shared.TrimAnalysis 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); diff --git a/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/linker/Linker.Dataflow/HandleCallAction.cs index de8dcf8f2..00371df07 100644 --- a/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -68,6 +68,17 @@ namespace ILLink.Shared.TrimAnalysis 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); diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 61325ae6b..8531974cc 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -289,7 +289,8 @@ namespace Mono.Linker.Dataflow 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 ()) { @@ -577,55 +578,6 @@ namespace Mono.Linker.Dataflow 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 // // static CreateInstance (System.Type type) diff --git a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index 955d7ba04..8ada1cff3 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -201,8 +201,7 @@ namespace ILLink.RoslynAnalyzer.Tests [Fact] public Task TypeBaseTypeDataFlow () { - // https://github.com/dotnet/linker/issues/2273 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] diff --git a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs index 0e12517ab..395ec25c8 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs @@ -164,6 +164,12 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] + public Task TypeBaseTypeUseViaReflection () + { + return RunTest (); + } + + [Fact] public Task TypeHierarchyReflectionWarnings () { // https://github.com/dotnet/linker/issues/2578 diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs index 6e1bf553d..353ac8968 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs @@ -62,12 +62,6 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] - public Task TypeBaseTypeUseViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] public Task TypeDelegator () { return RunTest (allowMissingWarnings: true); diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs index f1cfccc3a..646994d11 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs @@ -67,7 +67,11 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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 ()); |