From 6d6f2bd36c7d5e134177383958b02522b770bd04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 17 Dec 2024 15:34:09 +0100 Subject: [PATCH] Fix base type marking for DAM annotated classes (#110655) Fixes #104740, also implements #110563 for ILC. The annotation on DAM annotated classes gets triggered by calling GetType on a location typed as something DAM annotated (or deriving/implementing from it). The marking is done in layers so that warning suppressions can be properly applied. The bug was that we didn't walk down the hierarchy and assumed someone else will do it. Nobody did. I'm adding a new node type to do the marking. Previously, we only used one node for tracking. The node got dropped into the dependency graph when we saw GetType being called and it ensured the proper marking for that one type (not the bases). We also added conditional dependencies into MethodTables so that `Derived` can depend on this node of the `Base`. This ensured that if GetType was called on Base, we'd treat it same as GetType on Derived. This was resulting in marking too much and too little (we'd mark right away when we saw GetType call, irrespective of the MethodTable existence, and we wouldn't walk down to Base if the GetType was called on Derived. The fix is to use two node types. One simply tracks "GetType was called on something". It doesn't bring any other dependencies with it. We only use it to condition other nodes. The other node represents "the dependencies from the annotations". The way this works is: * We see GetType called on Base, so we add ObjectGetTypeCalledNode for Base. * We generate MethodTable for Derived, which adds a conditional dependency on ObjectGetTypeCalledNode of Derived if ObjectGetTypeCalledNode of Base is in the graph. (This ensures "walking up the hierarchy".) * MethodTable of Derived also adds a conditional dependency on ObjectGetTypeFlowDependenciesNode of Derived if ObjectGetTypeCalledNode of Derived is part of the graph. This will do the actual marking but only if the MethodTable and GetType call was seen. * ObjectGetTypeFlowDependenciesNode also "walks down the hierarchy" and makes sure ObjectGetTypeFlowDependenciesNode of all the bases and interfaces are present in the graph. This happens to also address #110563. Because of that, I had to update tests since ILC started trimming more stuff without seeing the type as constructed. --- .../Compiler/Dataflow/HandleCallAction.cs | 2 +- .../Dataflow/ReflectionMethodBodyScanner.cs | 19 +++ .../DependencyAnalysis/NodeFactory.cs | 11 ++ .../ObjectGetTypeCalledNode.cs | 40 ++++++ .../ObjectGetTypeFlowDependenciesNode.cs | 28 ++-- .../Compiler/UsageBasedMetadataManager.cs | 24 +++- .../ILCompiler.Compiler.csproj | 1 + .../SmokeTests/TrimmingBehaviors/Dataflow.cs | 5 +- .../DataFlow/ObjectGetTypeDataflow.cs | 44 +++++- .../Generics/InstantiatedGenericEquality.cs | 4 +- .../Reflection/ObjectGetType.cs | 130 +++++++++++++++++- .../TypeHierarchyReflectionWarnings.cs | 34 +++-- .../Reflection/TypeHierarchySuppressions.cs | 1 + .../RequiresCapability/RequiresOnClass.cs | 10 +- 14 files changed, 313 insertions(+), 40 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeCalledNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index 9ca8b98e9e135..d1a8db0c1b999 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -455,7 +455,7 @@ private partial bool TryHandleIntrinsic ( if (annotation != default) { - _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ObjectGetTypeFlowDependencies(closestMetadataType), "GetType called on this type"); + _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ObjectGetTypeCalled(closestMetadataType), "GetType called on this type"); } // Return a value which is "unknown type" with annotation. For now we'll use the return value node diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index ce3493118082a..4738e0755bf08 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -130,6 +130,25 @@ public static DependencyList ProcessTypeGetTypeDataflow(NodeFactory factory, Flo { DynamicallyAccessedMemberTypes annotation = flowAnnotations.GetTypeAnnotation(type); Debug.Assert(annotation != DynamicallyAccessedMemberTypes.None); + + // We're on an interface and we're processing annotations for the purposes of a object.GetType() call. + // Most of the annotations don't apply to the members of interfaces - the result of object.GetType() is + // never the interface type, it's a concrete type that implements the interface. Limit this to the only + // annotations that are applicable in this situation. + if (type.IsInterface) + { + // .All applies to interface members same as to the type + if (annotation != DynamicallyAccessedMemberTypes.All) + { + // Filter to the MemberTypes that apply to interfaces + annotation &= DynamicallyAccessedMemberTypes.Interfaces; + + // If we're left with nothing, we're done + if (annotation == DynamicallyAccessedMemberTypes.None) + return new DependencyList(); + } + } + var reflectionMarker = new ReflectionMarker(logger, factory, flowAnnotations, typeHierarchyDataFlowOrigin: type, enabled: true); // We need to apply annotations to this type, and its base/interface types (recursively) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 84e0418e3f9e4..d66f9fcc413a6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -377,6 +377,11 @@ private void CreateNodeCaches() return new GenericStaticBaseInfoNode(type); }); + _objectGetTypeCalled = new NodeCache(type => + { + return new ObjectGetTypeCalledNode(type); + }); + _objectGetTypeFlowDependencies = new NodeCache(type => { return new ObjectGetTypeFlowDependenciesNode(type); @@ -1147,6 +1152,12 @@ internal GenericStaticBaseInfoNode GenericStaticBaseInfo(MetadataType type) return _genericStaticBaseInfos.GetOrAdd(type); } + private NodeCache _objectGetTypeCalled; + internal ObjectGetTypeCalledNode ObjectGetTypeCalled(MetadataType type) + { + return _objectGetTypeCalled.GetOrAdd(type); + } + private NodeCache _objectGetTypeFlowDependencies; internal ObjectGetTypeFlowDependenciesNode ObjectGetTypeFlowDependencies(MetadataType type) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeCalledNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeCalledNode.cs new file mode 100644 index 0000000000000..1072172986318 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeCalledNode.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents the fact that object.GetType was called on a location typed as this + /// type or one of the subtypes of it. + /// + internal sealed class ObjectGetTypeCalledNode : DependencyNodeCore + { + private readonly MetadataType _type; + + public ObjectGetTypeCalledNode(MetadataType type) + { + _type = type; + } + + protected override string GetName(NodeFactory factory) + { + return $"Object.GetType called on {_type}"; + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs index a30e1a7dc3c55..49fa353f50e55 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectGetTypeFlowDependenciesNode.cs @@ -6,6 +6,8 @@ using ILCompiler.DependencyAnalysisFramework; +using ILLink.Shared.TrimAnalysis; + using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -25,21 +27,31 @@ public ObjectGetTypeFlowDependenciesNode(MetadataType type) protected override string GetName(NodeFactory factory) { - return $"Object.GetType dependencies called on {_type}"; + return $"Object.GetType dependencies for {_type}"; } public override IEnumerable GetStaticDependencies(NodeFactory factory) { var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + FlowAnnotations flowAnnotations = mdManager.FlowAnnotations; + + DependencyList result = Dataflow.ReflectionMethodBodyScanner.ProcessTypeGetTypeDataflow(factory, mdManager.FlowAnnotations, mdManager.Logger, _type); + + MetadataType baseType = _type.MetadataBaseType; + if (baseType != null && flowAnnotations.GetTypeAnnotation(baseType) != default) + { + result.Add(factory.ObjectGetTypeFlowDependencies(baseType), "Apply annotations to bases"); + } - // We don't mark any members on interfaces - these nodes are only used as conditional dependencies - // of other nodes. Calling `object.GetType()` on something typed as an interface will return - // something that implements the interface, not the interface itself. We're not reflecting on the - // interface. - if (_type.IsInterface) - return Array.Empty(); + foreach (DefType interfaceType in _type.RuntimeInterfaces) + { + if (flowAnnotations.GetTypeAnnotation(interfaceType) != default) + { + result.Add(factory.ObjectGetTypeFlowDependencies((MetadataType)interfaceType), "Apply annotations to interfaces"); + } + } - return Dataflow.ReflectionMethodBodyScanner.ProcessTypeGetTypeDataflow(factory, mdManager.FlowAnnotations, mdManager.Logger, _type); + return result; } public override bool InterestingForDynamicDependencyAnalysis => false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index a301d770dc018..fd0c4e025d65e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -408,9 +408,19 @@ public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedD { // Check to see if we have any dataflow annotations on the type. // The check below also covers flow annotations inherited through base classes and implemented interfaces. - if (type.IsDefType - && !type.IsInterface /* "IFoo x; x.GetType();" -> this doesn't actually return an interface type */ - && FlowAnnotations.GetTypeAnnotation(type) != default) + bool hasFlowAnnotations = type.IsDefType && FlowAnnotations.GetTypeAnnotation(type) != default; + + if (hasFlowAnnotations) + { + dependencies ??= new CombinedDependencyList(); + dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( + factory.ObjectGetTypeFlowDependencies((MetadataType)type), + factory.ObjectGetTypeCalled((MetadataType)type), + "Type exists and GetType called on it")); + } + + if (hasFlowAnnotations + && !type.IsInterface /* "IFoo x; x.GetType();" -> this doesn't actually return an interface type */) { // We have some flow annotations on this type. // @@ -428,8 +438,8 @@ public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedD // Ensure we have the flow dependencies. dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ObjectGetTypeFlowDependencies((MetadataType)type), - factory.ObjectGetTypeFlowDependencies((MetadataType)baseType), + factory.ObjectGetTypeCalled((MetadataType)type), + factory.ObjectGetTypeCalled((MetadataType)baseType), "GetType called on the base type")); // We don't have to follow all the bases since the base MethodTable will bubble this up @@ -444,8 +454,8 @@ public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedD // Ensure we have the flow dependencies. dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ObjectGetTypeFlowDependencies((MetadataType)type), - factory.ObjectGetTypeFlowDependencies((MetadataType)interfaceType), + factory.ObjectGetTypeCalled((MetadataType)type), + factory.ObjectGetTypeCalled((MetadataType)interfaceType), "GetType called on the interface")); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 29a917f01c68c..954ae4124058a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -428,6 +428,7 @@ + diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs index a75ca2bcd0c4f..dbdbcae08372e 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs @@ -565,10 +565,7 @@ public static void Run() // so that we can tests this doesn't lead to keeping the type. Assert.Equal(666, s_neverAllocatedTypeAskingForNonPublicMethods.GetType().CountMethods()); } - // This is not great - the GetType() call above "wished" NeverAllocatedTypeAskingForNonPublicMethods - // into existence, but it shouldn't have. We could do better here if this is a problem. - // If we do that, change this .NotNull to .Null. - Assert.NotNull(typeof(TestObjectGetTypeDataflow).GetNestedTypeSecretly(nameof(NeverAllocatedTypeAskingForNonPublicMethods))); + Assert.Null(typeof(TestObjectGetTypeDataflow).GetNestedTypeSecretly(nameof(NeverAllocatedTypeAskingForNonPublicMethods))); // Sanity check Assert.NotNull(typeof(TestObjectGetTypeDataflow).GetNestedTypeSecretly(nameof(TypeWithNonPublicMethodsKept))); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ObjectGetTypeDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ObjectGetTypeDataflow.cs index 71c1999c9600f..a90bee0d79c6e 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ObjectGetTypeDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ObjectGetTypeDataflow.cs @@ -18,6 +18,7 @@ public static void Main () { SealedConstructorAsSource.Test (); InstantiatedGenericAsSource.Test (); + InstantiatedGenericAsSourceInstantiated.Test (); EnumTypeSatisfiesPublicFields.Test (); EnumConstraintSatisfiesPublicFields.Test (); InstantiatedTypeParameterAsSource.Test (); @@ -50,15 +51,15 @@ class InstantiatedGenericAsSource { [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] class Generic { - [ExpectedWarning ("IL2112")] + [ExpectedWarning ("IL2112", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/110563")] [RequiresUnreferencedCode (nameof (KeptForMethodParameter))] public void KeptForMethodParameter () {} - [ExpectedWarning ("IL2112")] + [ExpectedWarning ("IL2112", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/110563")] [RequiresUnreferencedCode (nameof (KeptForField))] public void KeptForField () {} - [ExpectedWarning ("IL2112")] + [ExpectedWarning ("IL2112", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/110563")] [RequiresUnreferencedCode (nameof (KeptJustBecause))] public void KeptJustBecause () {} } @@ -82,6 +83,43 @@ public static void Test () } } + class InstantiatedGenericAsSourceInstantiated + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class Generic + { + [ExpectedWarning ("IL2112")] + [RequiresUnreferencedCode (nameof (KeptForMethodParameter))] + public void KeptForMethodParameter () { } + + [ExpectedWarning ("IL2112")] + [RequiresUnreferencedCode (nameof (KeptForField))] + public void KeptForField () { } + + [ExpectedWarning ("IL2112")] + [RequiresUnreferencedCode (nameof (KeptJustBecause))] + public void KeptJustBecause () { } + } + + static void TestMethodParameter (Generic instance) + { + instance.GetType ().GetMethod ("KeptForMethodParameter"); + } + + static Generic field = new Generic (); + + static void TestField () + { + field.GetType ().GetMethod ("KeptForField"); + } + + public static void Test () + { + TestMethodParameter (null); + TestField (); + } + } + class EnumTypeSatisfiesPublicFields { static void ParameterType (Enum instance) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs index 2c9b69c9df973..1fe5fb5be8521 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs @@ -18,11 +18,11 @@ public static void Main () class GenericReturnType { - [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute), By = Tool.Trimmer /* Type is needed due to IL metadata only */)] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] class ReturnType { - [Kept] + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] public void Method () { } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs index 58090e4ec1c51..0152323e9ef3c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs @@ -1582,16 +1582,46 @@ public static void Test () } } - class AnnotatedDerived + class AnnotatedBaseInstantiated { [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptMember (".ctor()")] class Base { [Kept] public void Method () { } } - [Kept (By = Tool.Trimmer /* The object.GetType() call is statically unreachable, this could be trimmed */)] + [Kept] + [KeptBaseType (typeof (Base), By = Tool.Trimmer)] + [KeptMember (".ctor()")] + class Derived : Base + { + } + + [Kept] + static Derived derivedInstance; + + [Kept] + public static void Test () + { + derivedInstance = new Derived (); + derivedInstance.GetType ().RequiresPublicMethods (); + } + } + + class AnnotatedDerived + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + class Base + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + public void Method () { } + } + + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] [KeptBaseType (typeof (Base), By = Tool.Trimmer)] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute), By = Tool.Trimmer)] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] @@ -1609,14 +1639,73 @@ public static void Test () } } + class AnnotatedDerivedInstantiated + { + [Kept] + [KeptMember (".ctor()")] + class Base + { + [Kept] + public void Method () { } + } + + [Kept] + [KeptBaseType (typeof (Base))] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptMember (".ctor()")] + class Derived : Base + { + } + + [Kept] + static Derived derivedInstance; + + [Kept] + public static void Test () + { + derivedInstance = new Derived (); + derivedInstance.GetType ().RequiresPublicMethods (); + } + } + class AnnotatedInterface + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute), By = Tool.Trimmer)] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + interface IBase + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + public void Method () { } + } + + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + [KeptMember (".ctor()", By = Tool.Trimmer)] + [KeptInterface (typeof (IBase), By = Tool.Trimmer)] + class Implementation : IBase + { + } + + [Kept] + static Implementation implementationInstance; + + [Kept] + public static void Test () + { + var a = implementationInstance as IBase; + implementationInstance.GetType ().RequiresPublicMethods (); + } + } + + class AnnotatedInterfaceInstantiated { [Kept] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] interface IBase { - [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/104740 */)] + [Kept] public void Method () { } } @@ -1640,6 +1729,35 @@ public static void Test () } class AnnotatedImplementation + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + interface IBase + { + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + public void Method () { } + } + + [Kept (By = Tool.Trimmer /* https://github.com/dotnet/runtime/issues/110563 */)] + [KeptMember (".ctor()", By = Tool.Trimmer)] + [KeptInterface (typeof (IBase), By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute), By = Tool.Trimmer)] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + class Implementation : IBase + { + } + + [Kept] + static Implementation implementationInstance; + + [Kept] + public static void Test () + { + var a = implementationInstance as IBase; + implementationInstance.GetType ().RequiresPublicMethods (); + } + } + + class AnnotatedImplementationInstantiated { [Kept] interface IBase @@ -1672,10 +1790,14 @@ public static void Test () [Kept] public static void Test () { - AnnotatedBase.Test (); ; + AnnotatedBase.Test (); + AnnotatedBaseInstantiated.Test (); AnnotatedDerived.Test (); + AnnotatedDerivedInstantiated.Test (); AnnotatedInterface.Test (); + AnnotatedInterfaceInstantiated.Test (); AnnotatedImplementation.Test (); + AnnotatedImplementationInstantiated.Test (); } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs index bded9f86c640e..1fe8f1fb82730 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs @@ -17,6 +17,7 @@ namespace Mono.Linker.Tests.Cases.Reflection [SkipKeptItemsValidation (By = Tool.NativeAot)] public class TypeHierarchyReflectionWarnings { + [ExpectedWarning ("IL2026", "--AnnotatedRUCPublicMethods--")] public static void Main () { annotatedBase.GetType ().RequiresPublicMethods (); @@ -45,8 +46,15 @@ public static void Main () var t7 = typeof (DerivedFromAnnotatedPublicParameterlessConstructor); annotatedRUCPublicMethods.GetType ().RequiresPublicMethods (); - // Instantiate this type just so its property getters are considered reachable - var b = new DerivedFromAnnotatedDerivedFromBase (); + // Instantiate these types just so things are considered reachable + _ = new DerivedFromAnnotatedDerivedFromBase (); + _ = new AnnotatedPublicMethods (); + _ = new AnnotatedPublicFields (); + _ = new AnnotatedPublicEvents (); + _ = new AnnotatedPublicProperties (); + _ = new AnnotatedInterfaces (); + _ = new AnnotatedPublicNestedTypes (); + _ = new AnnotatedRUCPublicMethods (); // Check that this field doesn't produce a warning even if it is kept // for some non-reflection access. @@ -62,8 +70,8 @@ public static void Main () RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Test (); UseByDerived.Test (); - CompilerGeneratedCodeRUC.Test (null); - CompilerGeneratedCodeDAM.Test (null); + CompilerGeneratedCodeRUC.Test (new CompilerGeneratedCodeRUC ()); + CompilerGeneratedCodeDAM.Test (new CompilerGeneratedCodeDAM ()); } [Kept] @@ -152,6 +160,7 @@ class DerivedFromAnnotatedAllWithInterface : AnnotatedAll, InterfaceImplementedB } [Kept] + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] class AnnotatedPublicMethods @@ -204,6 +213,7 @@ Type type } [Kept] + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] class AnnotatedPublicFields @@ -217,6 +227,7 @@ class AnnotatedPublicFields } [Kept] + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] class AnnotatedPublicProperties @@ -237,6 +248,7 @@ public static string DAMProperty { } [Kept] + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] class AnnotatedPublicEvents @@ -272,6 +284,7 @@ interface RequiredInterface } [Kept] + [KeptMember (".ctor()")] [KeptInterface (typeof (RequiredInterface))] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] @@ -497,6 +510,7 @@ public class BaseTypeOfNestedType public void RUCMethod () { } } + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] // Warnings about base types of nested types are shown at the (outer) type level. @@ -595,6 +609,7 @@ public class NestedType : AnnotatedBaseSharedByNestedTypes [RequiresUnreferencedCode ("--AnnotatedRUCPublicMethods--")] public class AnnotatedRUCPublicMethods { + [Kept] public AnnotatedRUCPublicMethods () { } [Kept] @@ -702,7 +717,7 @@ public class Base [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode ("--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--")] - [ExpectedWarning ("IL2112", "--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104740")] + [ExpectedWarning ("IL2112", "--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--")] public virtual void RUCVirtualMethod () { } } @@ -740,7 +755,7 @@ public interface Interface [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode ("--RUCOnVirtualOnAnnotatedInterface.Interface.RUCVirtualMethod--")] - [ExpectedWarning ("IL2112", "--RUCOnVirtualOnAnnotatedInterface.Interface.RUCVirtualMethod--", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104740")] + [ExpectedWarning ("IL2112", "--RUCOnVirtualOnAnnotatedInterface.Interface.RUCVirtualMethod--")] void RUCVirtualMethod () { } } @@ -778,7 +793,7 @@ public interface Interface [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode ("--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Interface.RUCVirtualMethod--")] - [ExpectedWarning ("IL2112", "--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Interface.RUCVirtualMethod--", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104740")] + [ExpectedWarning ("IL2112", "--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Interface.RUCVirtualMethod--")] void RUCVirtualMethod () { } } @@ -821,7 +836,7 @@ class AnnotatedBase [RequiresUnreferencedCode ("--AnnotatedBase.VirtualMethodWithRequires--")] [RequiresDynamicCode ("--AnnotatedBase.VirtualMethodWithRequires--")] [RequiresAssemblyFiles ("--AnnotatedBase.VirtualMethodWithRequires--")] - [ExpectedWarning ("IL2112", "--AnnotatedBase.VirtualMethodWithRequires--", Tool.Trimmer | Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104740")] + [ExpectedWarning ("IL2112", "--AnnotatedBase.VirtualMethodWithRequires--")] public virtual void VirtualMethodWithRequires () { } } @@ -900,6 +915,7 @@ public static void Test () class CompilerGeneratedBackingField { [Kept] + [KeptMember (".ctor()")] public class BaseWithField { [KeptBackingField] @@ -908,6 +924,7 @@ public class BaseWithField } [Kept] + [KeptMember (".ctor()")] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [KeptBaseType (typeof (BaseWithField))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] @@ -923,6 +940,7 @@ public class DerivedWithAnnotation : BaseWithField [Kept] public static void Test () { + derivedInstance = new DerivedWithAnnotation (); derivedInstance.GetType ().RequiresNonPublicFields (); } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs index 49d9b13bf9ca9..2120d4afea237 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs @@ -27,6 +27,7 @@ public static void Main () // derived types to access annotated methods without any warnings: derivedFromSuppressed.GetType ().GetMethod ("RUCDerivedMethod"); + _ = new AnnotatedAllSuppressed (); } [Kept] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs index f9a97cfffdccd..da4fbcd59c330 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs @@ -821,13 +821,15 @@ static void TestDAMAccessOnInstantiatedGeneric () } [ExpectedWarning ("IL2026", "--TestDAMOnTypeAccessInRUCScope--")] + [ExpectedWarning ("IL2026", "DAMAnnotatedClass.DAMAnnotatedClass()")] + [ExpectedWarning ("IL3050", "DAMAnnotatedClass.DAMAnnotatedClass()", Tool.NativeAot | Tool.Analyzer, "")] public static void Test () { TestDAMAccess (); TestDirectReflectionAccess (); TestDynamicDependencyAccess (); - TestDAMOnTypeAccess (null); - TestDAMOnTypeAccessInRUCScope (); + TestDAMOnTypeAccess (new DAMAnnotatedClass ()); + TestDAMOnTypeAccessInRUCScope (new DAMAnnotatedClassAccessedFromRUCScope ()); TestDAMAccessOnOpenGeneric (); TestDAMAccessOnInstantiatedGeneric (); } @@ -1208,12 +1210,14 @@ static void TestDAMOnTypeAccess (DAMAnnotatedClass instance) instance.GetType ().GetProperty ("publicProperty"); } + [ExpectedWarning ("IL2026", "DAMAnnotatedClass.DAMAnnotatedClass()")] + [ExpectedWarning ("IL3050", "DAMAnnotatedClass.DAMAnnotatedClass()", Tool.NativeAot | Tool.Analyzer, "")] public static void Test () { TestDAMAccess (); TestDirectReflectionAccess (); TestDynamicDependencyAccess (); - TestDAMOnTypeAccess (null); + TestDAMOnTypeAccess (new DAMAnnotatedClass ()); } }