From 8d9fa9c85f01a7ca6d2fbe0cc82402fafd7725af Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 21 Aug 2024 15:18:39 -0700 Subject: [PATCH 1/4] Add test for generic equality bug --- .../Reflection/TypeUsedViaReflection.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index b76e2a90e7a60d..8337089a42e97b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -60,6 +60,7 @@ public static void Main () TestInvalidTypeCombination (); TestEscapedTypeName (); AssemblyTypeResolutionBehavior.Test (); + InstantiatedGenericEquality.Test (); } [Kept] @@ -686,6 +687,46 @@ class PointerElementGenericArgumentType {} class ByRefElementGenericArgumentType {} } + [Kept] + class InstantiatedGenericEquality + { + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class GenericReturnType + { + [Kept] + public void Method () { } + } + + [Kept] + static GenericReturnType GetGenericReturnType () => default; + + // Regression test for an issue where ILLink's representation of a generic instantiated type + // was using reference equality. The test uses a lambda to ensure that it goes through the + // interprocedural analysis code path that merges patterns and relies on a correct implementation + // of equality. + [Kept] + static void TestGenericReturnType () + { + var instance = GetGenericReturnType (); + + var lambda = + [UnexpectedWarning ("IL2075", Tool.Trimmer, "")] + () => { + var type = instance.GetType (); + type.GetMethod ("Method"); + }; + + lambda (); + } + + [Kept] + public static void Test () + { + TestGenericReturnType (); + } + } + [Kept] static void RequireConstructor ( [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] From 4cf45575a996294b0a3ed66a30fd46e64f3d9e4a Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 21 Aug 2024 15:33:27 -0700 Subject: [PATCH 2/4] Fix TypeProxy equality --- .../Linker.Dataflow/AttributeDataFlow.cs | 4 ++-- .../src/linker/Linker.Dataflow/FieldValue.cs | 4 ++-- .../linker/Linker.Dataflow/FlowAnnotations.cs | 14 +++++++------- .../GenericArgumentDataFlow.cs | 2 +- .../Linker.Dataflow/HandleCallAction.cs | 10 +++++----- .../Linker.Dataflow/MethodBodyScanner.cs | 6 +++--- .../Linker.Dataflow/MethodParameterValue.cs | 6 +++--- .../Linker.Dataflow/MethodReturnValue.cs | 8 ++++---- ...RequireDynamicallyAccessedMembersAction.cs | 5 ++++- .../TrimAnalysisAssignmentPattern.cs | 2 +- .../src/linker/Linker.Dataflow/TypeProxy.cs | 19 +++++++++++++++---- .../Reflection/TypeUsedViaReflection.cs | 1 - 12 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Dataflow/AttributeDataFlow.cs b/src/tools/illink/src/linker/Linker.Dataflow/AttributeDataFlow.cs index fa2aa697d9dba2..cf615f67925493 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/AttributeDataFlow.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/AttributeDataFlow.cs @@ -56,7 +56,7 @@ MultiValue GetValueForCustomAttributeArgument (CustomAttributeArgument argument) TypeDefinition? referencedType = ((TypeReference) argument.Value).ResolveToTypeDefinition (_context); return referencedType == null ? UnknownValue.Instance - : new SystemTypeValue (referencedType); + : new SystemTypeValue (new (referencedType, _context)); } if (argument.Type.MetadataType == MetadataType.String) @@ -69,7 +69,7 @@ MultiValue GetValueForCustomAttributeArgument (CustomAttributeArgument argument) void RequireDynamicallyAccessedMembers (in DiagnosticContext diagnosticContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue) { var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (reflectionMarker, diagnosticContext); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (_context, reflectionMarker, diagnosticContext); requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/FieldValue.cs b/src/tools/illink/src/linker/Linker.Dataflow/FieldValue.cs index 4655f3a9a13cfd..349401f1fecec1 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/FieldValue.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/FieldValue.cs @@ -16,9 +16,9 @@ namespace ILLink.Shared.TrimAnalysis /// internal sealed partial record FieldValue { - public FieldValue (FieldReference fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public FieldValue (FieldReference fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, ITryResolveMetadata resolver) { - StaticType = fieldToLoad.FieldType.InflateFrom (fieldToLoad.DeclaringType as IGenericInstance); + StaticType = new (fieldToLoad.FieldType.InflateFrom (fieldToLoad.DeclaringType as IGenericInstance), resolver); Field = fieldToLoad; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs index 0894e1aae8e57e..d9a61e69ea056a 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -710,7 +710,7 @@ internal partial bool MethodRequiresDataFlowAnalysis (MethodProxy method) #pragma warning disable CA1822 // Mark members as static - Should be an instance method for consistency internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => MethodReturnValue.Create (method, isNewObj, dynamicallyAccessedMemberTypes); + => MethodReturnValue.Create (method, isNewObj, dynamicallyAccessedMemberTypes, _context); #pragma warning restore CA1822 // Mark members as static internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, bool isNewObj) @@ -726,7 +726,7 @@ internal partial GenericParameterValue GetGenericParameterValue (GenericParamete #pragma warning disable CA1822 // Mark members as static - Should be an instance method for consistency internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - => new (param.ParameterType, param, dynamicallyAccessedMemberTypes); + => new (param.ParameterType, param, dynamicallyAccessedMemberTypes, _context); #pragma warning restore CA1822 // Mark members as static internal partial MethodParameterValue GetMethodParameterValue (ParameterProxy param) @@ -737,7 +737,7 @@ internal partial MethodParameterValue GetMethodThisParameterValue (MethodProxy m { if (!method.HasImplicitThis ()) throw new InvalidOperationException ($"Cannot get 'this' parameter of method {method.GetDisplayName ()} with no 'this' parameter."); - return new MethodParameterValue (method.Method.DeclaringType, new ParameterProxy (method, (ParameterIndex) 0), dynamicallyAccessedMemberTypes); + return new MethodParameterValue (method.Method.DeclaringType, new ParameterProxy (method, (ParameterIndex) 0), dynamicallyAccessedMemberTypes, _context); } #pragma warning restore CA1822 // Mark members as static @@ -755,7 +755,7 @@ internal SingleValue GetFieldValue (FieldReference field) => field.Name switch { "EmptyTypes" when field.DeclaringType.IsTypeOf (WellKnownType.System_Type) => ArrayValue.Create (0, field.DeclaringType), "Empty" when field.DeclaringType.IsTypeOf (WellKnownType.System_String) => new KnownStringValue (string.Empty), - _ => new FieldValue (field, GetFieldAnnotation (field)) + _ => new FieldValue (field, GetFieldAnnotation (field), _context) }; internal SingleValue GetTypeValueFromGenericArgument (TypeReference genericArgument) @@ -769,18 +769,18 @@ internal SingleValue GetTypeValueFromGenericArgument (TypeReference genericArgum var innerGenericArgument = (genericArgument as IGenericInstance)?.GenericArguments.FirstOrDefault (); switch (innerGenericArgument) { case GenericParameter gp: - return new NullableValueWithDynamicallyAccessedMembers (genericArgumentType, + return new NullableValueWithDynamicallyAccessedMembers (new (genericArgumentType, _context), new GenericParameterValue (gp, _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (gp))); case TypeReference underlyingType: if (underlyingType.ResolveToTypeDefinition (_context) is TypeDefinition underlyingTypeDefinition) - return new NullableSystemTypeValue (genericArgumentType, new SystemTypeValue (underlyingTypeDefinition)); + return new NullableSystemTypeValue (new (genericArgumentType, _context), new SystemTypeValue (new (underlyingTypeDefinition, _context))); else return UnknownValue.Instance; } } // All values except for Nullable, including Nullable<> (with no type arguments) - return new SystemTypeValue (genericArgumentType); + return new SystemTypeValue (new (genericArgumentType, _context)); } else { return UnknownValue.Instance; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs b/src/tools/illink/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs index 9d358a339ef70d..e3e7d7f3ef3ed4 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/GenericArgumentDataFlow.cs @@ -37,7 +37,7 @@ public void ProcessGenericArgumentDataFlow (GenericParameter genericParameter, T void RequireDynamicallyAccessedMembers (in DiagnosticContext diagnosticContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue) { var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (reflectionMarker, diagnosticContext); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (_context, reflectionMarker, diagnosticContext); requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index a337e999506f8b..64cbf525911e11 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -41,7 +41,7 @@ public HandleCallAction ( _diagnosticContext = diagnosticContext; _callingMethodDefinition = callingMethodDefinition; _annotations = context.Annotations.FlowAnnotations; - _requireDynamicallyAccessedMembersAction = new (reflectionMarker, diagnosticContext); + _requireDynamicallyAccessedMembersAction = new (context, reflectionMarker, diagnosticContext); } private partial bool TryHandleIntrinsic ( @@ -150,7 +150,7 @@ private partial bool TryHandleIntrinsic ( // This can be seen a little bit as a violation of the annotation, but we already have similar cases // where a parameter is annotated and if something in the method sets a specific known type to it // we will also make it just work, even if the annotation doesn't match the usage. - AddReturnValue (new SystemTypeValue (staticType)); + AddReturnValue (new SystemTypeValue (new (staticType, _context))); } else if (staticTypeDef.IsTypeOf ("System", "Enum")) { AddReturnValue (_context.Annotations.FlowAnnotations.GetMethodReturnValue (calledMethod, _isNewObj, DynamicallyAccessedMemberTypes.PublicFields)); } else { @@ -215,13 +215,13 @@ private partial IEnumerable GetMethodsOnTypeHie private partial IEnumerable GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags) { foreach (var nestedType in type.Type.GetNestedTypesOnType (_context, t => t.Name == name, bindingFlags)) - yield return new SystemTypeValue (new TypeProxy (nestedType)); + yield return new SystemTypeValue (new TypeProxy (nestedType, _context)); } private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType) { if (type.Type.ResolveToTypeDefinition (_context)?.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) { - baseType = new TypeProxy (baseTypeDefinition); + baseType = new TypeProxy (baseTypeDefinition, _context); return true; } @@ -250,7 +250,7 @@ private partial bool TryResolveTypeNameForCreateInstanceAndMark (in MethodProxy return false; } - resolvedType = new TypeProxy (resolvedTypeDefinition); + resolvedType = new TypeProxy (resolvedTypeDefinition, _context); return true; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs index 97a70d088bc909..849342bbe395f3 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs @@ -791,12 +791,12 @@ void ScanLdtoken (Instruction operation, Stack currentStack) if (typeReference is IGenericInstance instance && resolvedDefinition.IsTypeOf (WellKnownType.System_Nullable_T)) { switch (instance.GenericArguments[0]) { case GenericParameter genericParam: - var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers (new TypeProxy (resolvedDefinition), + var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers (new TypeProxy (resolvedDefinition, _context), new RuntimeTypeHandleForGenericParameterValue (genericParam)); currentStack.Push (new StackSlot (nullableDam)); return; case TypeReference underlyingTypeReference when ResolveToTypeDefinition (underlyingTypeReference) is TypeDefinition underlyingType: - var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue (new TypeProxy (resolvedDefinition), new SystemTypeValue (underlyingType)); + var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue (new TypeProxy (resolvedDefinition, _context), new SystemTypeValue (new (underlyingType, _context))); currentStack.Push (new StackSlot (nullableType)); return; default: @@ -804,7 +804,7 @@ void ScanLdtoken (Instruction operation, Stack currentStack) return; } } else { - var typeHandle = new RuntimeTypeHandleValue (new TypeProxy (resolvedDefinition)); + var typeHandle = new RuntimeTypeHandleValue (new TypeProxy (resolvedDefinition, _context)); currentStack.Push (new StackSlot (typeHandle)); return; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/MethodParameterValue.cs b/src/tools/illink/src/linker/Linker.Dataflow/MethodParameterValue.cs index d1ea4dfd6547a7..94ef79bada2178 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/MethodParameterValue.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/MethodParameterValue.cs @@ -3,9 +3,9 @@ using System.Diagnostics.CodeAnalysis; using ILLink.Shared.TypeSystemProxy; +using Mono.Linker; using TypeReference = Mono.Cecil.TypeReference; - namespace ILLink.Shared.TrimAnalysis { @@ -14,9 +14,9 @@ namespace ILLink.Shared.TrimAnalysis /// internal partial record MethodParameterValue { - public MethodParameterValue (TypeReference? staticType, ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public MethodParameterValue (TypeReference? staticType, ParameterProxy param, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, ITryResolveMetadata resolver) { - StaticType = staticType == null ? null : new (staticType); + StaticType = staticType == null ? null : new (staticType, resolver); DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; Parameter = param; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/MethodReturnValue.cs b/src/tools/illink/src/linker/Linker.Dataflow/MethodReturnValue.cs index e8843976d528c9..4063034dab0188 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/MethodReturnValue.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/MethodReturnValue.cs @@ -19,17 +19,17 @@ namespace ILLink.Shared.TrimAnalysis /// internal partial record MethodReturnValue { - public static MethodReturnValue Create (MethodProxy method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public static MethodReturnValue Create (MethodProxy method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, ITryResolveMetadata resolver) { Debug.Assert (!isNewObj || method.Definition.IsConstructor, "isNewObj can only be true for constructors"); var methodRef = method.Method; var staticType = isNewObj ? methodRef.DeclaringType : methodRef.ReturnType.InflateFrom (methodRef as IGenericInstance ?? methodRef.DeclaringType as IGenericInstance); - return new MethodReturnValue (staticType, method.Definition, dynamicallyAccessedMemberTypes); + return new MethodReturnValue (staticType, method.Definition, dynamicallyAccessedMemberTypes, resolver); } - private MethodReturnValue (TypeReference? staticType, MethodDefinition method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + private MethodReturnValue (TypeReference? staticType, MethodDefinition method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, ITryResolveMetadata resolver) { - StaticType = staticType == null ? null : new (staticType); + StaticType = staticType == null ? null : new (staticType, resolver); Method = method; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/RequireDynamicallyAccessedMembersAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/RequireDynamicallyAccessedMembersAction.cs index cae166abe0c5ea..46b618cf29e481 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/RequireDynamicallyAccessedMembersAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/RequireDynamicallyAccessedMembersAction.cs @@ -11,12 +11,15 @@ namespace ILLink.Shared.TrimAnalysis { internal partial struct RequireDynamicallyAccessedMembersAction { + readonly ITryResolveMetadata _resolver; readonly ReflectionMarker _reflectionMarker; public RequireDynamicallyAccessedMembersAction ( + ITryResolveMetadata resolver, ReflectionMarker reflectionMarker, in DiagnosticContext diagnosticContext) { + _resolver = resolver; _reflectionMarker = reflectionMarker; _diagnosticContext = diagnosticContext; } @@ -24,7 +27,7 @@ public RequireDynamicallyAccessedMembersAction ( public partial bool TryResolveTypeNameAndMark (string typeName, bool needsAssemblyName, out TypeProxy type) { if (_reflectionMarker.TryResolveTypeNameAndMark (typeName, _diagnosticContext, needsAssemblyName, out TypeDefinition? foundType)) { - type = new (foundType); + type = new (foundType, _resolver); return true; } else { type = default; diff --git a/src/tools/illink/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs b/src/tools/illink/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs index 2d76af158a09c2..855c0883dd1905 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs @@ -49,7 +49,7 @@ public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, LinkCo if (targetValue is not ValueWithDynamicallyAccessedMembers targetWithDynamicallyAccessedMembers) throw new NotImplementedException (); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (reflectionMarker, diagnosticContext); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (context, reflectionMarker, diagnosticContext); requireDynamicallyAccessedMembersAction.Invoke (sourceValue, targetWithDynamicallyAccessedMembers); } } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/TypeProxy.cs b/src/tools/illink/src/linker/Linker.Dataflow/TypeProxy.cs index 5a87941e1c1969..f157de79ed398e 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/TypeProxy.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/TypeProxy.cs @@ -1,17 +1,20 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Collections.Immutable; using Mono.Cecil; using Mono.Linker; namespace ILLink.Shared.TypeSystemProxy { - internal readonly partial struct TypeProxy + internal readonly partial struct TypeProxy : IEquatable { - public TypeProxy (TypeReference type) => Type = type; - - public static implicit operator TypeProxy (TypeReference type) => new (type); + public TypeProxy (TypeReference type, ITryResolveMetadata resolver) + { + Type = type; + this.resolver = resolver; + } internal partial ImmutableArray GetGenericParameters () { @@ -26,6 +29,8 @@ internal partial ImmutableArray GetGenericParameters () return builder.ToImmutableArray (); } + private readonly ITryResolveMetadata resolver; + public TypeReference Type { get; } public string Name { get => Type.Name; } @@ -39,5 +44,11 @@ internal partial ImmutableArray GetGenericParameters () public string GetDisplayName () => Type.GetDisplayName (); public override string ToString () => Type.ToString (); + + public bool Equals (TypeProxy other) => TypeReferenceEqualityComparer.AreEqual (Type, other.Type, resolver); + + public override bool Equals (object? o) => o is TypeProxy other && Equals (other); + + public override int GetHashCode () => TypeReferenceEqualityComparer.GetHashCodeFor (Type); } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 8337089a42e97b..ba03a369fc77be 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -711,7 +711,6 @@ static void TestGenericReturnType () var instance = GetGenericReturnType (); var lambda = - [UnexpectedWarning ("IL2075", Tool.Trimmer, "")] () => { var type = instance.GetType (); type.GetMethod ("Method"); From 7252a954c7551d04f7b3a386ebe0cf31d366a37b Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 21 Aug 2024 15:48:35 -0700 Subject: [PATCH 3/4] Move test --- .../Generics/InstantiatedGenericEquality.cs | 51 +++++++++++++++++++ .../Reflection/TypeUsedViaReflection.cs | 40 --------------- 2 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs 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 new file mode 100644 index 00000000000000..12e55811cba0fa --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/InstantiatedGenericEquality.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Generics +{ + [ExpectedNoWarnings] + class InstantiatedGenericEquality + { + public static void Main () + { + GenericReturnType.Test (); + } + + class GenericReturnType + { + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class ReturnType + { + [Kept] + public void Method () { } + } + + [Kept] + static ReturnType GetGenericReturnType () => default; + + // Regression test for an issue where ILLink's representation of a generic instantiated type + // was using reference equality. The test uses a lambda to ensure that it goes through the + // interprocedural analysis code path that merges patterns and relies on a correct implementation + // of equality. + [Kept] + public static void Test () + { + var instance = GetGenericReturnType (); + + var lambda = + () => { + var type = instance.GetType (); + type.GetMethod ("Method"); + }; + + lambda (); + } + } + } +} \ No newline at end of file diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index ba03a369fc77be..b76e2a90e7a60d 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -60,7 +60,6 @@ public static void Main () TestInvalidTypeCombination (); TestEscapedTypeName (); AssemblyTypeResolutionBehavior.Test (); - InstantiatedGenericEquality.Test (); } [Kept] @@ -687,45 +686,6 @@ class PointerElementGenericArgumentType {} class ByRefElementGenericArgumentType {} } - [Kept] - class InstantiatedGenericEquality - { - [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] - class GenericReturnType - { - [Kept] - public void Method () { } - } - - [Kept] - static GenericReturnType GetGenericReturnType () => default; - - // Regression test for an issue where ILLink's representation of a generic instantiated type - // was using reference equality. The test uses a lambda to ensure that it goes through the - // interprocedural analysis code path that merges patterns and relies on a correct implementation - // of equality. - [Kept] - static void TestGenericReturnType () - { - var instance = GetGenericReturnType (); - - var lambda = - () => { - var type = instance.GetType (); - type.GetMethod ("Method"); - }; - - lambda (); - } - - [Kept] - public static void Test () - { - TestGenericReturnType (); - } - } - [Kept] static void RequireConstructor ( [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] From 4d54ae37328344cbf90c08a0b5e0e242ab378627 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 26 Aug 2024 13:37:44 -0700 Subject: [PATCH 4/4] Add analyzer test --- .../GenericsTests.cs | 17 +++++++++++++++++ .../GenericsTests.g.cs | 2 -- .../Generics/InstantiatedGenericEquality.cs | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/GenericsTests.cs diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/GenericsTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/GenericsTests.cs new file mode 100644 index 00000000000000..45e6b37dfc24c1 --- /dev/null +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/GenericsTests.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests +{ + public sealed partial class GenericsTests : LinkerTestBase + { + protected override string TestSuiteName => "Generics"; + + [Fact] + public Task InstantiatedGenericEquality () + { + return RunTest (); + } + } +} diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs index 7042a4ff54f540..373e72a087b5f7 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs @@ -7,8 +7,6 @@ namespace ILLink.RoslynAnalyzer.Tests public sealed partial class GenericsTests : LinkerTestBase { - protected override string TestSuiteName => "Generics"; - [Fact] public Task ArrayVariantCasting () { 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 12e55811cba0fa..2c9b69c9df9734 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 @@ -48,4 +48,4 @@ public static void Test () } } } -} \ No newline at end of file +}