From 34db15f35ba4d319b1aca2899004500eb8832f4f Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 20 Jan 2023 02:18:33 -0800 Subject: [PATCH 1/5] Implements generic parameter data flow mostly trhough data flow scanner Previously generic data flow was done from generic dictionary nodes. Problem with that approach is that there's no origin information at that point. The warnings can't point to the place where the problematic instantiation is in the code - we only know that it exists. Aside from it being unfriendly for the users, it means any RUC or suppressions don't work on these warnings the same way they do in linker/analyzer. This change modifies the logic to tag the method as "needs data flow" whenever we spot an instantiation of an annotated generic in it somewhere. Then the actualy validation/marking is done from data flow using the trim analysis patterns. The only exception to this is generic data flow for base types and interface implementations, that one is done on the EEType nodes. Note that AOT implements a much more precise version of the generic data flow validation as compared to linker/analyzer. See the big comment at the bening of GenericParameterWarningLocation.cs for how that works. --- .../Common/Compiler/DisplayNameHelpers.cs | 5 +- .../Compiler/Dataflow/FlowAnnotations.cs | 34 +- .../Dataflow/GenericArgumentDataFlow.cs | 83 +- .../Compiler/Dataflow/MethodBodyScanner.cs | 57 +- .../Dataflow/ReflectionMethodBodyScanner.cs | 81 +- ...alysisGenericInstantiationAccessPattern.cs | 51 + .../Dataflow/TrimAnalysisPatternStore.cs | 17 +- .../TrimAnalysisReflectionAccessPattern.cs | 21 +- .../ConstructedEETypeNode.cs | 4 +- .../Compiler/DependencyAnalysis/EETypeNode.cs | 4 +- .../GenericDefinitionEETypeNode.cs | 4 +- .../Compiler/MetadataManager.cs | 2 +- .../Compiler/UsageBasedMetadataManager.cs | 105 +- .../ILCompiler.Compiler.csproj | 1 + .../GenericParameterWarningLocation.cs | 1245 +++++++++++++++++ 15 files changed, 1584 insertions(+), 130 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs diff --git a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs index df62b988f20e19..31ae384844d762 100644 --- a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs +++ b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs @@ -89,7 +89,10 @@ public static string GetDisplayName(this MethodDesc method) if (method.Signature.Length > 0) { for (int i = 0; i < method.Signature.Length - 1; i++) - sb.Append(method.Signature[i].GetDisplayNameWithoutNamespace()).Append(','); + { + TypeDesc instantiatedType = method.Signature[i].InstantiateSignature(method.OwningType.Instantiation, method.Instantiation); + sb.Append(instantiatedType.GetDisplayNameWithoutNamespace()).Append(','); + } sb.Append(method.Signature[method.Signature.Length - 1].GetDisplayNameWithoutNamespace()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs index 0cf5a42421be89..c4e164791dc625 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -46,8 +46,8 @@ public bool RequiresDataflowAnalysis(MethodDesc method) try { method = method.GetTypicalMethodDefinition(); - return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var methodAnnotations) - && (methodAnnotations.ReturnParameterAnnotation != DynamicallyAccessedMemberTypes.None || methodAnnotations.ParameterAnnotations != null); + TypeAnnotations typeAnnotations = GetAnnotations(method.OwningType); + return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(method, out _); } catch (TypeSystemException) { @@ -73,7 +73,8 @@ public bool RequiresDataflowAnalysis(FieldDesc field) try { field = field.GetTypicalFieldDefinition(); - return GetAnnotations(field.OwningType).TryGetAnnotation(field, out _); + TypeAnnotations typeAnnotations = GetAnnotations(field.OwningType); + return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(field, out _); } catch (TypeSystemException) { @@ -105,6 +106,31 @@ public bool HasAnyAnnotations(TypeDesc type) } } + public bool HasGenericParameterAnnotation(TypeDesc type) + { + try + { + return GetAnnotations(type.GetTypeDefinition()).HasGenericParameterAnnotation(); + } + catch (TypeSystemException) + { + return false; + } + } + + public bool HasGenericParameterAnnotation(MethodDesc method) + { + try + { + method = method.GetTypicalMethodDefinition(); + return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation) && annotation.GenericParameterAnnotations != null; + } + catch (TypeSystemException) + { + return false; + } + } + internal DynamicallyAccessedMemberTypes GetParameterAnnotation(ParameterProxy param) { MethodDesc method = param.Method.Method.GetTypicalMethodDefinition(); @@ -884,6 +910,8 @@ public bool TryGetAnnotation(GenericParameterDesc genericParameter, out Dynamica return false; } + + public bool HasGenericParameterAnnotation() => _genericParameterAnnotations != null; } private readonly struct MethodAnnotations diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs index 555091d04a7693..eeecfa6c0f623d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs @@ -18,45 +18,72 @@ namespace ILCompiler.Dataflow { - public readonly struct GenericArgumentDataFlow + public static class GenericArgumentDataFlow { - private readonly Logger _logger; - private readonly NodeFactory _factory; - private readonly FlowAnnotations _annotations; - private readonly MessageOrigin _origin; + public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin, TypeDesc type) + { + var diagnosticContext = new DiagnosticContext( + origin, + !logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), + logger); + var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlow: false, enabled: true); + + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type); + + if (reflectionMarker.Dependencies.Count > 0) + { + if (dependencies == null) + dependencies = reflectionMarker.Dependencies; + else + dependencies.AddRange(reflectionMarker.Dependencies); + } + } - public GenericArgumentDataFlow(Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin) + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, TypeDesc type) { - _logger = logger; - _factory = factory; - _annotations = annotations; - _origin = origin; + if (type.HasInstantiation) + { + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (typeDefinition != type) + { + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation); + } + } } - public DependencyList ProcessGenericArgumentDataFlow(GenericParameterDesc genericParameter, TypeDesc genericArgument) + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, MethodDesc method) { - var genericParameterValue = _annotations.GetGenericParameterValue(genericParameter); - Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); + if (method.HasInstantiation) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (typicalMethod != method) + { + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation); + } + } - MultiValue genericArgumentValue = _annotations.GetTypeValueFromGenericArgument(genericArgument); + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method.OwningType); + } - var diagnosticContext = new DiagnosticContext( - _origin, - _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), - _logger); - return RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue, genericParameter.GetDisplayName()); + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, FieldDesc field) + { + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field.OwningType); } - private DependencyList RequireDynamicallyAccessedMembers( - in DiagnosticContext diagnosticContext, - in MultiValue value, - ValueWithDynamicallyAccessedMembers targetValue, - string reason) + private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, Instantiation instantiation, Instantiation typicalInstantiation) { - var reflectionMarker = new ReflectionMarker(_logger, _factory, _annotations, typeHierarchyDataFlowOrigin: null, enabled: true); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, reason); - requireDynamicallyAccessedMembersAction.Invoke(value, targetValue); - return reflectionMarker.Dependencies; + for (int i = 0; i < instantiation.Length; i++) + { + var genericParameter = (GenericParameterDesc)typicalInstantiation[i]; + if (reflectionMarker.Annotations.GetGenericParameterAnnotation(genericParameter) != default) + { + var genericParameterValue = reflectionMarker.Annotations.GetGenericParameterValue(genericParameter); + Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); + MultiValue genericArgumentValue = reflectionMarker.Annotations.GetTypeValueFromGenericArgument(instantiation[i]); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, new GenericParameterOrigin(genericParameter)); + requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue); + } + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index 70640274fc3b44..f4beb9857e5927 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -800,7 +800,7 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset); // If the return value is a reference, treat it as the value itself for now // We can handle ref return values better later - ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(retValue.Value, locals, ref interproceduralState)); + ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(methodBody, offset, retValue.Value, locals, ref interproceduralState)); ValidateNoReferenceToReference(locals, methodBody, offset); } ClearStack(ref currentStack); @@ -947,23 +947,24 @@ private void ScanLdtoken(MethodIL methodBody, int offset, object operand, Stack< var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(new TypeProxy(type), new RuntimeTypeHandleForGenericParameterValue(genericParam)); currentStack.Push(new StackSlot(nullableDam)); - return; + break; case MetadataType underlyingType: var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue(new TypeProxy(type), new SystemTypeValue(underlyingType)); currentStack.Push(new StackSlot(nullableType)); - return; + break; default: PushUnknown(currentStack); - return; + break; } } else { var typeHandle = new RuntimeTypeHandleValue(new TypeProxy(type)); currentStack.Push(new StackSlot(typeHandle)); - return; } } + + HandleTypeReflectionAccess(methodBody, offset, type); } else if (operand is MethodDesc method) { @@ -1026,7 +1027,7 @@ protected void StoreInReference(MultiValue target, MultiValue source, MethodIL m StoreMethodLocalValue(locals, source, localReference.LocalIndex, curBasicBlock); break; case FieldReferenceValue fieldReference - when GetFieldValue(fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: + when GetFieldValue(method, offset, fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: HandleStoreField(method, offset, fieldValue, source); break; case ParameterReferenceValue parameterReference @@ -1038,7 +1039,7 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal HandleStoreMethodReturnValue(method, offset, methodReturnValue, source); break; case FieldValue fieldValue: - HandleStoreField(method, offset, fieldValue, DereferenceValue(source, locals, ref ipState)); + HandleStoreField(method, offset, fieldValue, DereferenceValue(method, offset, source, locals, ref ipState)); break; case IValueWithStaticType valueWithStaticType: if (valueWithStaticType.StaticType is not null && FlowAnnotations.IsTypeInterestingForDataflow(valueWithStaticType.StaticType)) @@ -1057,7 +1058,25 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal } - protected abstract MultiValue GetFieldValue(FieldDesc field); + /// + /// GetFieldValue is called every time the scanner needs to represent a value of the field + /// either as a source or target. It is not called when just a reference to field is created, + /// But if such reference is dereferenced then it will get called. + /// It is NOT called for hoisted locals. + /// + /// + /// There should be no need to perform checks for hoisted locals. All of our reflection checks are based + /// on an assumption that problematic things happen because of running code. Doing things purely in the type system + /// (declaring new types which are never instantiated, declaring fields which are never assigned to, ...) + /// don't cause problems (or better way, they won't show observable behavioral differences). + /// Typically that would mean that accessing fields is also an uninteresting operation, unfortunately + /// static fields access can cause execution of static .cctor and that is running code -> possible problems. + /// So we have to track accesses in that case. + /// Hoisted locals are fields on closure classes/structs which should not have static .ctors, so we don't + /// need to track those. It makes the design a bit cleaner because hoisted locals are purely handled in here + /// and don't leak over to the reflection handling code in any way. + /// + protected abstract MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field); private void ScanLdfld( MethodIL methodBody, @@ -1083,7 +1102,7 @@ private void ScanLdfld( } else { - value = GetFieldValue(field); + value = GetFieldValue(methodBody, offset, field); } currentStack.Push(new StackSlot(value)); } @@ -1119,7 +1138,7 @@ private void ScanStfld( return; } - foreach (var value in GetFieldValue(field)) + foreach (var value in GetFieldValue(methodBody, offset, field)) { // GetFieldValue may return different node types, in which case they can't be stored to. // At least not yet. @@ -1127,7 +1146,7 @@ private void ScanStfld( continue; // Incomplete handling of ref fields -- if we're storing a reference to a value, pretend it's just the value - MultiValue valueToStore = DereferenceValue(valueToStoreSlot.Value, locals, ref interproceduralState); + MultiValue valueToStore = DereferenceValue(methodBody, offset, valueToStoreSlot.Value, locals, ref interproceduralState); HandleStoreField(methodBody, offset, fieldValue, valueToStore); } @@ -1163,7 +1182,12 @@ private ValueNodeList PopCallArguments( return methodParams; } - internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicBlockPair?[] locals, ref InterproceduralState interproceduralState) + internal MultiValue DereferenceValue( + MethodIL methodBody, + int offset, + MultiValue maybeReferenceValue, + ValueBasicBlockPair?[] locals, + ref InterproceduralState interproceduralState) { MultiValue dereferencedValue = MultiValueLattice.Top; foreach (var value in maybeReferenceValue) @@ -1175,7 +1199,7 @@ internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicB dereferencedValue, CompilerGeneratedState.IsHoistedLocal(fieldReferenceValue.FieldDefinition) ? interproceduralState.GetHoistedLocal(new HoistedLocalKey(fieldReferenceValue.FieldDefinition)) - : GetFieldValue(fieldReferenceValue.FieldDefinition)); + : GetFieldValue(methodBody, offset, fieldReferenceValue.FieldDefinition)); break; case ParameterReferenceValue parameterReferenceValue: dereferencedValue = MultiValue.Meet( @@ -1224,6 +1248,11 @@ protected void AssignRefAndOutParameters( } } + /// + /// Called when type is accessed directly (basically only ldtoken) + /// + protected abstract void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType); + /// /// Called to handle reflection access to a method without any other specifics (ldtoken or ldftn for example) /// @@ -1260,7 +1289,7 @@ private void HandleCall( var dereferencedMethodParams = new List(); foreach (var argument in methodArguments) - dereferencedMethodParams.Add(DereferenceValue(argument, locals, ref interproceduralState)); + dereferencedMethodParams.Add(DereferenceValue(callingMethodBody, offset, argument, locals, ref interproceduralState)); MultiValue methodReturnValue; bool handledFunction = HandleCall( callingMethodBody, 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 7a73ba40d0812f..83fa616dc495e8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -139,7 +139,19 @@ protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue(P private MethodParameterValue GetMethodParameterValue(ParameterProxy parameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) => _annotations.GetMethodParameterValue(parameter, dynamicallyAccessedMemberTypes); - protected override MultiValue GetFieldValue(FieldDesc field) => _annotations.GetFieldValue(field); + /// + /// GetFieldValue is called every time the scanner needs to represent a value of the field + /// either as a source or target. It is not called when just a reference to field is created, + /// But if such reference is dereferenced then it will get called. + /// + protected override MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field) + { + _origin = _origin.WithInstructionOffset(methodBody, offset); + + ProcessGenericArgumentDataFlow(field); + + return _annotations.GetFieldValue(field); + } private void HandleStoreValueWithDynamicallyAccessedMembers(MethodIL methodBody, int offset, ValueWithDynamicallyAccessedMembers targetValue, MultiValue sourceValue, string reason) { @@ -172,6 +184,35 @@ protected override void HandleFieldReflectionAccess(MethodIL methodBody, int off TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin)); } + protected override void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType) + { + // Note that ldtoken alone is technically a reflection access to the type + // it doesn't lead to full reflection marking of the type + // since we implement full dataflow for type values and accesses to them. + _origin = _origin.WithInstructionOffset(methodBody, offset); + + // Only check for generic instantiations. + ProcessGenericArgumentDataFlow(accessedType); + } + + protected override void HandleMethodReflectionAccess(MethodIL methodBody, int offset, MethodDesc accessedMethod) + { + _origin = _origin.WithInstructionOffset(methodBody, offset); + + TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedMethod, _origin)); + + ProcessGenericArgumentDataFlow(accessedMethod); + } + + protected override void HandleFieldReflectionAccess(MethodIL methodBody, int offset, FieldDesc accessedField) + { + _origin = _origin.WithInstructionOffset(methodBody, offset); + + TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin)); + + ProcessGenericArgumentDataFlow(accessedField); + } + public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out MultiValue methodReturnValue) { Debug.Assert(callingMethodBody.OwningMethod == _origin.MemberDefinition); @@ -201,6 +242,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet _origin )); + ProcessGenericArgumentDataFlow(calledMethod); + var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: false, _logger); return HandleCall( callingMethodBody, @@ -634,6 +677,42 @@ private void HandleAssignmentPattern( TrimAnalysisPatterns.Add(new TrimAnalysisAssignmentPattern(value, targetValue, origin, reason)); } + private void ProcessGenericArgumentDataFlow(MethodDesc method) + { + // We only need to validate static methods and then all generic methods + // Instance non-generic methods don't need validation because the creation of the instance + // is the place where the validation will happen. + if (!method.Signature.IsStatic && !method.HasInstantiation && !method.IsConstructor) + return; + + if ((method.HasInstantiation && _annotations.HasGenericParameterAnnotation(method)) || + (method.OwningType.HasInstantiation && _annotations.HasGenericParameterAnnotation(method.OwningType))) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(method, _origin)); + } + } + + private void ProcessGenericArgumentDataFlow(FieldDesc field) + { + // We only need to validate static field accesses, instance field accesses don't need generic parameter validation + // because the create of the instance would do that instead. + if (!field.IsStatic) + return; + + if (field.OwningType.HasInstantiation && _annotations.HasGenericParameterAnnotation(field.OwningType)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(field, _origin)); + } + } + + private void ProcessGenericArgumentDataFlow(TypeDesc type) + { + if (type.HasInstantiation && _annotations.HasGenericParameterAnnotation(type)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(type, _origin)); + } + } + private static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDangerousMethod, out bool aotUnsafeDelegate) { if (!calledMethod.IsPInvoke) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs new file mode 100644 index 00000000000000..a9a7bbb9b044b7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.Logging; +using ILLink.Shared.TrimAnalysis; +using Internal.TypeSystem; + +#nullable enable + +namespace ILCompiler.Dataflow +{ + public readonly record struct TrimAnalysisGenericInstantiationAccessPattern + { + public TypeSystemEntity Entity { init; get; } + public MessageOrigin Origin { init; get; } + + internal TrimAnalysisGenericInstantiationAccessPattern(TypeSystemEntity entity, MessageOrigin origin) + { + Entity = entity; + Origin = origin; + } + + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. + + public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger) + { + var diagnosticContext = new DiagnosticContext( + Origin, + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute), + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute), + logger); + + switch (Entity) + { + case TypeDesc type: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type); + break; + + case MethodDesc method: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method); + break; + + case FieldDesc field: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field); + break; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs index 85c9aa72eee383..a241f386489d1e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs @@ -16,6 +16,7 @@ public readonly struct TrimAnalysisPatternStore private readonly Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern> AssignmentPatterns; private readonly Dictionary MethodCallPatterns; private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern> ReflectionAccessPatterns; + private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern> GenericInstantiations; private readonly ValueSetLattice Lattice; private readonly Logger _logger; @@ -24,6 +25,7 @@ public TrimAnalysisPatternStore(ValueSetLattice lattice, Logger log AssignmentPatterns = new Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern>(); MethodCallPatterns = new Dictionary(); ReflectionAccessPatterns = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern>(); + GenericInstantiations = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern>(); Lattice = lattice; _logger = logger; } @@ -60,8 +62,16 @@ public void Add(TrimAnalysisReflectionAccessPattern pattern) { ReflectionAccessPatterns.TryAdd((pattern.Origin, pattern.Entity), pattern); - // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity - // and there's only one way to "reflection access" an entity. + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. + } + + public void Add(TrimAnalysisGenericInstantiationAccessPattern pattern) + { + GenericInstantiations.TryAdd((pattern.Origin, pattern.Entity), pattern); + + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. } public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker) @@ -74,6 +84,9 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker) foreach (var pattern in ReflectionAccessPatterns.Values) pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger); + + foreach (var pattern in GenericInstantiations.Values) + pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs index 8a07621f565396..31217b0580742d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs @@ -1,9 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using ILCompiler.Logging; +using ILLink.Shared.TrimAnalysis; using Internal.TypeSystem; +#nullable enable + namespace ILCompiler.Dataflow { public readonly record struct TrimAnalysisReflectionAccessPattern @@ -17,12 +21,25 @@ internal TrimAnalysisReflectionAccessPattern(TypeSystemEntity entity, MessageOri Origin = origin; } - // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity // and there's only one way to "reflection access" an entity. public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger) { - reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, Entity); + switch (Entity) + { + case MethodDesc method: + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method, new MethodOrigin(Origin.MemberDefinition as MethodDesc)); + break; + + case FieldDesc field: + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field, new MethodOrigin(Origin.MemberDefinition as MethodDesc)); + break; + + default: + Debug.Fail($"Unsupported entity for reflection access pattern: {Entity}"); + break; + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 6d734d92ee6e0d..1e9e3144d2be0d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -75,8 +75,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } } - // Ask the metadata manager if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + // Ask the metadata manager if we have any dependencies due to the presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index e6950d68d7dedf..0f1b9951a2c1ee 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -596,8 +596,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact if (!ConstructedEETypeNode.CreationAllowed(_type)) { // If necessary MethodTable is the highest load level for this type, ask the metadata manager - // if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _type); + // if we have any dependencies due to presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencies, factory, _type); // If necessary MethodTable is the highest load level, consider this a module use if(_type is MetadataType mdType) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index f6397e07c2d033..904475d484c88d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -24,8 +24,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact { DependencyList dependencyList = null; - // Ask the metadata manager if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + // Ask the metadata manager if we have any dependencies due to the presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); return dependencyList; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index d5d2d7bdfd3861..680d2b34dfeb82 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -449,7 +449,7 @@ protected virtual void GetMetadataDependenciesDueToReflectability(ref Dependency /// /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes. /// - public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { MetadataCategory category = GetMetadataCategory(type); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 04d765c00e4c78..90efd3aa6a5070 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -403,6 +403,38 @@ private static bool IsTrimmableAssembly(ModuleDesc assembly) return false; } + public override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); + + try + { + if (type.HasBaseType) + { + GetDataFlowDependenciesForInstantiation(ref dependencies, factory, type.BaseType, type); + } + + foreach (var runtimeInterface in type.RuntimeInterfaces) + { + GetDataFlowDependenciesForInstantiation(ref dependencies, factory, runtimeInterface, type); + } + } + catch (TypeSystemException) + { + // Wasn't able to do dataflow because of missing references or something like that. + // This likely won't compile either, so we don't care about missing dependencies. + } + } + + private void GetDataFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, TypeDesc contextType) + { + if (type.HasInstantiation && FlowAnnotations.HasGenericParameterAnnotation(type)) + { + TypeDesc instantiatedType = type.InstantiateSignature(contextType.Instantiation, Instantiation.Empty); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, Logger, factory, FlowAnnotations, new Logging.MessageOrigin(contextType), instantiatedType); + } + } + public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) { // Note: these are duplicated with the checks in GetConditionalDependenciesDueToEETypePresence @@ -575,27 +607,10 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen if (scanReflection) { - if (methodIL != null && FlowAnnotations.RequiresDataflowAnalysis(method)) + if (methodIL != null && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations, method)) { AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters"); } - - if ((method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any))) - { - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - Debug.Assert(typicalMethod != method); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); - } - - TypeDesc owningType = method.OwningType; - if (owningType.HasInstantiation && !owningType.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - TypeDesc owningTypeDefinition = owningType.GetTypeDefinition(); - Debug.Assert(owningType != owningTypeDefinition); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningTypeDefinition.Instantiation, owningType); - } } if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod) @@ -776,52 +791,8 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac return null; } - private void GetFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, Instantiation instantiation, Instantiation typicalInstantiation, TypeSystemEntity source) - { - for (int i = 0; i < instantiation.Length; i++) - { - var genericParameter = (GenericParameterDesc)typicalInstantiation[i]; - if (FlowAnnotations.GetGenericParameterAnnotation(genericParameter) != default) - { - try - { - var deps = (new ILCompiler.Dataflow.GenericArgumentDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(source))).ProcessGenericArgumentDataFlow(genericParameter, instantiation[i]); - if (deps.Count > 0) - { - if (dependencies == null) - dependencies = deps; - else - dependencies.AddRange(deps); - } - } - catch (TypeSystemException) - { - // Wasn't able to do dataflow because of missing references or something like that. - // This likely won't compile either, so we don't care about missing dependencies. - } - } - } - } - public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - TypeDesc owningType = method.OwningType; - - if (FlowAnnotations.HasAnyAnnotations(owningType)) - { - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - Debug.Assert(typicalMethod != method); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); - - if (owningType.HasInstantiation) - { - // Since this also introduces a new type instantiation into the system, collect the dependencies for that too. - // We might not see the instantiated type elsewhere. - GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningType.GetTypeDefinition().Instantiation, method); - } - } - // Presence of code might trigger the reflectability dependencies. if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0) { @@ -829,16 +800,6 @@ public override void GetDependenciesForGenericDictionary(ref DependencyList depe } } - public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - if (FlowAnnotations.HasAnyAnnotations(type)) - { - TypeDesc typeDefinition = type.GetTypeDefinition(); - Debug.Assert(type != typeDefinition); - GetFlowDependenciesForInstantiation(ref dependencies, factory, type.Instantiation, typeDefinition.Instantiation, type); - } - } - public bool GeneratesAttributeMetadata(TypeDesc attributeType) { var ecmaType = attributeType.GetTypeDefinition() as EcmaType; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 33c4e33bde641b..289d7d0b5325a4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -378,6 +378,7 @@ + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs new file mode 100644 index 00000000000000..8aff7ec7ad2f7d --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -0,0 +1,1245 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Security.Policy; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // NativeAOT differences in behavior: + // + // Validation of generic parameters only matters if the instantiation can be used to run code with the substituted type. + // So for generic methods the validation has to happen basically always (since any access to the method can lead to the code + // of the method executing eventually). + // For generic types though the situation is different. Code on the type can only run if the type is instantiated (new) + // or if static members are accessed on it (method calls, or fields accesses both can lead to static .cctor execution). + // Others usages of the type cannot themselves lead to code execution in the type, and thus don't need to be validated. + // Currently linker and analyzer both validate every time there's a type occurrence in the code. + // NativeAOT on the other hand only validates the cases which can lead to code execution (this is partially because the compiler + // doesn't care about the type in other situations really). + // So for example local variables of a given type, or method parameters of that type alone will not cause code execution + // inside that type and thus won't be validated by NativeAOT compiler. + // + // Below this explanation/fact is referred to as "NativeAOT_StorageSpaceType" + // Storage space - declaring a storage space as having a specific type doesn't in itself do anything with that type as per + // the above description. + + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GenericParameterWarningLocation + { + public static void Main () + { + TypeInheritance.Test (); + TypeImplementingInterface.Test (); + MethodParametersAndReturn.Test (); + FieldDefinition.Test (); + PropertyDefinition.Test (); + MethodBody.Test (); + } + + class TypeInheritance + { + class BaseWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + public static void GetMethods() + { + typeof (TPublicMethods).GetMethods (); + } + } + + class BaseWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + // No warning - annotations applied + class DerivedWithSpecificType : BaseWithPublicMethods { } + + // No warning - annotations match + class DerivedWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] TAllMethods> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithNoAnnotations + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithMismatchAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + class DerivedWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithTwo + { } + + class DerivedWithTwoMatching< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : BaseWithTwo + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithOnlyStaticMethodReference : BaseWithPublicMethods + { + // The method body in this case looks like: + // BaseWithPublicMethods.GetMethods () + // The type instantiation needs to be validated and in this case it produces a warning. + // This is no different from the same code being part of a completely unrelate method/class. + // So the fact that this is in derived class has no impact on the validation in this case. + [ExpectedWarning ("IL2091")] + public static void GetDerivedMethods () => GetMethods (); + } + + public static void Test () + { + Type t; + t = typeof (DerivedWithSpecificType); + t = typeof (DerivedWithMatchingAnnotation<>); + t = typeof (DerivedWithNoAnnotations<>); + t = typeof (DerivedWithMismatchAnnotation<>); + t = typeof (DerivedWithOneMismatch<>); + t = typeof (DerivedWithTwoMatching<,>); + + DerivedWithOnlyStaticMethodReference.GetDerivedMethods (); + } + } + + class TypeImplementingInterface + { + interface IWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> { } + + interface IWithPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> { } + + // No warning - annotations applied + class ImplementsWithSpecificType : IWithPublicMethods, IWithPublicFields { } + + // No warning - matching annotations + class ImplementsWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] TAll> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : IWithPublicMethods, IWithPublicFields + { } + + public static void Test () + { + // Instantiate the types + new ImplementsWithSpecificType (); + new ImplementsWithMatchingAnnotation (); + new ImplementsWithOneMismatch (); + new ImplementsWithTwoMismatches (); + + // Also reference the interfaces, otherwise they could be trimmed + Type t; + t = typeof (IWithPublicMethods<>); + t = typeof (IWithPublicFields<>); + } + } + + //.NativeAOT: Method parameter types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class MethodParametersAndReturn + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + static void MethodWithSpecificType (TypeWithPublicMethods one, IWithTwo two) { } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MethodWithOneMismatch (TypeWithPublicMethods one) { } + + [ExpectedWarning ("IL2091", nameof (IWithTwo), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MethodWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + (IWithTwo two, TypeWithPublicMethods one) + { } + + static TypeWithPublicMethods MethodWithSpecificReturnType () => null; + + static TypeWithPublicMethods MethodWithMatchingReturn<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods MethodWithOneMismatchReturn () => null; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo MethodWithTwoMismatchesInReturn< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () => null; + + public static void Test () + { + MethodWithSpecificType (null, null); + MethodWithOneMismatch (null); + MethodWithTwoMismatches (null, null); + + MethodWithSpecificReturnType (); + MethodWithMatchingReturn (); + MethodWithOneMismatchReturn (); + MethodWithTwoMismatchesInReturn (); + } + } + + //.NativeAOT: Field types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class FieldDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods _field1; + static TypeWithPublicMethods _field2; + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods _field3; + + public static void Test () + { + _field1 = null; + _field2 = null; + _field3 = null; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo _field; + + public static void Test () + { + _field = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + //.NativeAOT: Property types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + // In case of trimmer/aot it's even less important because properties don't exist in IL really + // and thus no code can manipulate them directly - only through reflection. + class PropertyDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods Property1 { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + static TypeWithPublicMethods Property2 { + get; + set; + } + + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods Property3 { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + public static void Test () + { + Property1 = Property1; + Property2 = Property2; + Property3 = Property3; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + // The warnings are generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo Property { + // Getter is trimmed and doesn't produce any warning + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + public static void Test () + { + Property = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + class MethodBody + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> : Exception + { + public static void Method () { } + public void InstanceMethod () { } + + public static string Field; + public string InstanceField; + + public static string Property { get; set; } + } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + public static void Method () { } + public void InstanceMethod (); + + public static string Field; + + public static string Property { get; set; } + } + + class TypeWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> : Exception + { } + + static void MethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { } + + void InstanceMethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { } + + static void MethodWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> () + { } + + static MethodBody GetInstance () => null; + + static TypeWithPublicMethods GetInstanceForTypeWithPublicMethods () => null; + + class TypeOf + { + static void AccessOpenTypeDefinition () + { + // Accessing the open type definition should not do anything on its own - just validating that it doesn't break anything + Type t = typeof (TypeWithPublicMethods<>); + t = typeof (IWithTwo<,>); + } + + static void SpecificType () + { + Type t = typeof (TypeWithPublicMethods); + t = typeof (IWithTwo); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Type t = typeof (TypeWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Type t = typeof (TypeWithPublicMethods); // Warn + t = typeof (TypeWithPublicMethods); // No warn + t = typeof (TypeWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Type t = typeof (IWithTwo); + } + + public static void Test () + { + AccessOpenTypeDefinition (); + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class MethodCallOnGenericMethod + { + static void SpecificType () + { + MethodWithPublicMethods (); + MethodWithTwo (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + MethodWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + MethodWithPublicMethods (); // Warn + MethodWithPublicMethods (); // No warn + MethodWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + MethodWithTwo (); + } + + [ExpectedWarning ("IL2091")] + static void InstanceMethodMismatch () + { + GetInstance ().InstanceMethodWithPublicMethods (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + InstanceMethodMismatch (); + } + } + + class MethodCallOnGenericType + { + static void SpecificType () + { + TypeWithPublicMethods.Method (); + IWithTwo.Method (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods.Method (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods.Method (); // Warn + TypeWithPublicMethods.Method (); // No warn + TypeWithPublicMethods.Method (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo.Method (); + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void InstanceMethodMismatch () + { + TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); + instance.InstanceMethod (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + InstanceMethodMismatch (); + } + } + + class FieldAccessOnGenericType + { + static void SpecificType () + { + _ = TypeWithPublicMethods.Field; + IWithTwo.Field = ""; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + _ = TypeWithPublicMethods.Field; + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameField< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + _ = TypeWithPublicMethods.Field; // Warn + TypeWithPublicMethods.Field = ""; // No warn + TypeWithPublicMethods.Field = ""; // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + _ = IWithTwo.Field; + } + + // The local variable + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void InstanceFieldMismatch () + { + TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); + _ = instance.InstanceField; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameField (); + TwoMismatchesInOneStatement (); + InstanceFieldMismatch (); + } + } + + //.NativeAOT: Local variable types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class LocalVariable + { + static void SpecificType () + { + TypeWithPublicMethods t = null; + IWithTwo i = null; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods t = null; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods t1 = null; // Warn + TypeWithPublicMethods t2 = null; // No warn + TypeWithPublicMethods t3 = null; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo i = null; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericMethod + { + static void SpecificType () + { + var a = new Action (MethodWithPublicMethods); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (MethodWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (MethodWithPublicMethods); // Warn + var a2 = new Action (MethodWithPublicMethods); // No warn + var a3 = new Action (MethodWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (MethodWithTwo); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericType + { + static void SpecificType () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (TypeWithPublicMethods.Method); // Warn + var a2 = new Action (TypeWithPublicMethods.Method); // No warn + var a3 = new Action (TypeWithPublicMethods.Method); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (IWithTwo.Method); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOnGenericMethod + { + static void SpecificType () + { + Expression a = () => MethodWithPublicMethods (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression a = () => MethodWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression a = () => MethodWithPublicMethods (); // Warn + a = () => MethodWithPublicMethods (); // No warn + a = () => MethodWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression a = () => MethodWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfMethodOnGenericType + { + static void SpecificType () + { + Expression a = () => TypeWithPublicMethods.Method (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression a = () => TypeWithPublicMethods.Method (); + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression a = () => TypeWithPublicMethods.Method (); // Warn + a = () => TypeWithPublicMethods.Method (); // No warn + a = () => TypeWithPublicMethods.Method (); // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression a = () => IWithTwo.Method (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfFieldOnGenericType + { + static void SpecificType () + { + Expression> a = () => TypeWithPublicMethods.Field; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression> a = () => TypeWithPublicMethods.Field; + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken field + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameField< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression> a = () => TypeWithPublicMethods.Field; // Warn + a = () => TypeWithPublicMethods.Field; // No warn + a = () => TypeWithPublicMethods.Field; // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken field + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression> a = () => IWithTwo.Field; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameField (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfPropertyOnGenericType + { + static void SpecificType () + { + Expression> a = () => TypeWithPublicMethods.Property; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression> a = () => TypeWithPublicMethods.Property; + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method (getter) + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameProperty< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression> a = () => TypeWithPublicMethods.Property; // Warn + a = () => TypeWithPublicMethods.Property; // No warn + a = () => TypeWithPublicMethods.Property; // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method (getter) + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression> a = () => IWithTwo.Property; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameProperty (); + TwoMismatchesInOneStatement (); + } + } + + class CreateInstance + { + static void SpecificType () + { + object a = new TypeWithPublicMethods (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = new TypeWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = new TypeWithPublicMethods (); // Warn + object a2 = new TypeWithPublicMethods (); // No warn + object a3 = new TypeWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = new TypeWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + //.NativeAOT: Checking an instance for its type is not interesting until something creates an instance of that type + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class IsInstance + { + static object _value = null; + + static void SpecificType () + { + bool a = _value is TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + bool a = _value is TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + bool a1 = _value is TypeWithPublicMethods; // Warn + bool a2 = _value is TypeWithPublicMethods; // No warn + bool a3 = _value is TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + bool a = _value is TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + // This is basically the same operation as IsInstance + class AsType + { + static object _value = null; + + static void SpecificType () + { + object a = _value as TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = _value as TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = _value as TypeWithPublicMethods; // Warn + object a2 = _value as TypeWithPublicMethods; // No warn + object a3 = _value as TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = _value as TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + //.NativeAOT: Exception types are effectively very similar to local variable or method parameters. + // and are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class ExceptionCatch + { + static void SpecificType () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + // This is basically the same as IsInstance and thus not dangerous + class ExceptionFilter + { + static void SpecificType () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + public static void Test () + { + TypeOf.Test (); + MethodCallOnGenericMethod.Test (); + MethodCallOnGenericType.Test (); + FieldAccessOnGenericType.Test (); + LocalVariable.Test (); + DelegateUsageOnGenericMethod.Test (); + DelegateUsageOnGenericType.Test (); + LdTokenOnGenericMethod.Test (); + LdTokenOfMethodOnGenericType.Test (); + LdTokenOfFieldOnGenericType.Test (); + LdTokenOfPropertyOnGenericType.Test (); + CreateInstance.Test (); + IsInstance.Test (); + AsType.Test (); + ExceptionCatch.Test (); + ExceptionFilter.Test (); + } + } + + class TestType { } + + static void DoNothing () { } + } +} From f699343e9f6d12ea701ba0645500575fd00eaaba Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 20 Jan 2023 03:06:34 -0800 Subject: [PATCH 2/5] Fix build after rebase --- .../Compiler/Dataflow/GenericArgumentDataFlow.cs | 4 ++-- .../Compiler/Dataflow/ReflectionMethodBodyScanner.cs | 12 ------------ .../Dataflow/TrimAnalysisReflectionAccessPattern.cs | 5 ++--- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs index eeecfa6c0f623d..6d7b357a9a6e18 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs @@ -26,7 +26,7 @@ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencie origin, !logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), logger); - var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlow: false, enabled: true); + var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlowOrigin: null, enabled: true); ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type); @@ -80,7 +80,7 @@ private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticC var genericParameterValue = reflectionMarker.Annotations.GetGenericParameterValue(genericParameter); Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); MultiValue genericArgumentValue = reflectionMarker.Annotations.GetTypeValueFromGenericArgument(instantiation[i]); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, new GenericParameterOrigin(genericParameter)); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, genericParameter.GetDisplayName()); requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue); } } 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 83fa616dc495e8..8b54be44762117 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -172,18 +172,6 @@ protected override void HandleStoreParameter(MethodIL methodBody, int offset, Me protected override void HandleStoreMethodReturnValue(MethodIL methodBody, int offset, MethodReturnValue returnValue, MultiValue valueToStore) => HandleStoreValueWithDynamicallyAccessedMembers(methodBody, offset, returnValue, valueToStore, returnValue.Method.GetDisplayName()); - protected override void HandleMethodReflectionAccess(MethodIL methodBody, int offset, MethodDesc accessedMethod) - { - _origin = _origin.WithInstructionOffset(methodBody, offset); - TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedMethod, _origin)); - } - - protected override void HandleFieldReflectionAccess(MethodIL methodBody, int offset, FieldDesc accessedField) - { - _origin = _origin.WithInstructionOffset(methodBody, offset); - TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin)); - } - protected override void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType) { // Note that ldtoken alone is technically a reflection access to the type diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs index 31217b0580742d..8f40b94c02d811 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using ILCompiler.Logging; -using ILLink.Shared.TrimAnalysis; using Internal.TypeSystem; #nullable enable @@ -29,11 +28,11 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger switch (Entity) { case MethodDesc method: - reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method, new MethodOrigin(Origin.MemberDefinition as MethodDesc)); + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method); break; case FieldDesc field: - reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field, new MethodOrigin(Origin.MemberDefinition as MethodDesc)); + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field); break; default: From a6cec9ecf9b1cf19e0ae6db34ddafa3ec9740d8b Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:00:15 -0800 Subject: [PATCH 3/5] Add GenericParameterDataFlow tests - change test infra Changes the expected warning validation to use tokens to compare message origins (same reason as with Kept validation - consistently converting things to string is hard) --- .../DataFlow/GenericParameterDataFlow.cs | 892 ++++++++++++++++++ .../GenericParameterWarningLocation.cs | 7 +- .../TestCasesRunner/AssemblyChecker.cs | 39 - .../TestCasesRunner/AssemblyQualifiedToken.cs | 51 + .../TestCasesRunner/ResultChecker.cs | 11 +- 5 files changed, 957 insertions(+), 43 deletions(-) create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs new file mode 100644 index 00000000000000..aa80500a606f4c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -0,0 +1,892 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using BindingFlags = System.Reflection.BindingFlags; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // NativeAOT differences in behavior: + // + // See the description on top of GenericParameterWarningLocation for the expected differences in behavior + // for NativeAOT. + // The tests affected by this are marked with "NativeAOT_StorageSpaceType" + // + + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GenericParameterDataFlow + { + public static void Main () + { + TestSingleGenericParameterOnType (); + TestMultipleGenericParametersOnType (); + TestBaseTypeGenericRequirements (); + TestDeepNestedTypesWithGenerics (); + TestInterfaceTypeGenericRequirements (); + TestTypeGenericRequirementsOnMembers (); + TestPartialInstantiationTypes (); + + TestSingleGenericParameterOnMethod (); + TestMultipleGenericParametersOnMethod (); + TestMethodGenericParametersViaInheritance (); + + TestNewConstraintSatisfiesParameterlessConstructor (); + TestStructConstraintSatisfiesParameterlessConstructor (); + TestUnmanagedConstraintSatisfiesParameterlessConstructor (); + + TestGenericParameterFlowsToField (); + TestGenericParameterFlowsToReturnValue (); + + TestNoWarningsInRUCMethod (); + TestNoWarningsInRUCType (); + } + + static void TestSingleGenericParameterOnType () + { + TypeRequiresNothing.Test (); + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresPublicFieldsPassThrough.Test (); + TypeRequiresNothingPassThrough.Test (); + } + + static void TestGenericParameterFlowsToReturnValue () + { + _ = TypeRequiresPublicFields.ReturnRequiresPublicFields (); + _ = TypeRequiresPublicFields.ReturnRequiresPublicMethods (); + _ = TypeRequiresPublicFields.ReturnRequiresNothing (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type FieldRequiresPublicFields; + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type FieldRequiresPublicMethods; + + static Type FieldRequiresNothing; + + + class TypeRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + [ExpectedWarning ("IL2087", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [RequiresUnreferencedCode ("message")] + public static void RUCTest () + { + typeof (T).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2089", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (FieldRequiresPublicMethods))] + public static void TestFields () + { + FieldRequiresPublicFields = typeof (T); + FieldRequiresPublicMethods = typeof (T); + FieldRequiresNothing = typeof (T); + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public static Type ReturnRequiresPublicFields () + { + return typeof (T); + } + + [ExpectedWarning ("IL2088", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (ReturnRequiresPublicMethods))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type ReturnRequiresPublicMethods () + { + return typeof (T); + } + public static Type ReturnRequiresNothing () + { + return typeof (T); + } + } + + class TypeRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + } + + class TypeRequiresNothing + { + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + } + + class TypeRequiresPublicFieldsPassThrough< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TSource> + { + [ExpectedWarning ("IL2091", nameof (TSource), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeRequiresPublicFieldsPassThrough", + "T", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeRequiresPublicMethods")] + public static void Test () + { + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresNothing.Test (); + } + } + + class TypeRequiresNothingPassThrough + { + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicFields))] + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods))] + public static void Test () + { + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresNothing.Test (); + } + } + + static void TestBaseTypeGenericRequirements () + { + new DerivedTypeWithInstantiatedGenericOnBase (); + new DerivedTypeWithInstantiationOverSelfOnBase (); + new DerivedTypeWithOpenGenericOnBase (); + TestDerivedTypeWithOpenGenericOnBaseWithRUCOnBase (); + TestDerivedTypeWithOpenGenericOnBaseWithRUCOnDerived (); + new DerivedTypeWithOpenGenericOnBaseWithRequirements (); + } + + /// + /// Adding a comment to verify that analyzer doesn't null ref when trying to analyze + /// generic parameter in cref comments on a class + /// + /// + class GenericBaseTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + public GenericBaseTypeWithRequirements () + { + typeof (T).RequiresPublicFields (); + } + } + + class DerivedTypeWithInstantiatedGenericOnBase : GenericBaseTypeWithRequirements + { + } + + class DerivedTypeWithInstantiationOverSelfOnBase : GenericBaseTypeWithRequirements + { + } + + [ExpectedWarning ("IL2091", nameof (GenericBaseTypeWithRequirements))] + class DerivedTypeWithOpenGenericOnBase : GenericBaseTypeWithRequirements + { + // Analyzer does not see the base class constructor + [ExpectedWarning ("IL2091", nameof (GenericBaseTypeWithRequirements), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public DerivedTypeWithOpenGenericOnBase () { } + } + + static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnBase () + { + new DerivedTypeWithOpenGenericOnBaseWithRUCOnBase (); + } + + // TODO !!! + [ExpectedWarning ("IL2109", nameof (BaseTypeWithOpenGenericDAMTAndRUC), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2091", nameof (BaseTypeWithOpenGenericDAMTAndRUC))] + class DerivedTypeWithOpenGenericOnBaseWithRUCOnBase : BaseTypeWithOpenGenericDAMTAndRUC + { + [ExpectedWarning ("IL2091", nameof (DerivedTypeWithOpenGenericOnBaseWithRUCOnBase), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", nameof (BaseTypeWithOpenGenericDAMTAndRUC), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public DerivedTypeWithOpenGenericOnBaseWithRUCOnBase () { } + } + + [RequiresUnreferencedCode ("RUC")] + class BaseTypeWithOpenGenericDAMTAndRUC<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> { } + + + [ExpectedWarning ("IL2026", nameof (DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived))] + static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnDerived () + { + new DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived (); + } + [ExpectedWarning ("IL2091", nameof (BaseTypeWithOpenGenericDAMT))] + [RequiresUnreferencedCode ("RUC")] + class DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived : BaseTypeWithOpenGenericDAMT + { + } + + class BaseTypeWithOpenGenericDAMT<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> { } + + + class DerivedTypeWithOpenGenericOnBaseWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + : GenericBaseTypeWithRequirements + { + } + + static void TestMultipleGenericParametersOnType () + { + MultipleTypesWithDifferentRequirements.TestMultiple (); + MultipleTypesWithDifferentRequirements.TestFields (); + MultipleTypesWithDifferentRequirements.TestMethods (); + MultipleTypesWithDifferentRequirements.TestBoth (); + MultipleTypesWithDifferentRequirements.TestNothing (); + } + + class MultipleTypesWithDifferentRequirements< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> + { + public static void TestMultiple () + { + typeof (TFields).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + typeof (TMethods).RequiresNone (); + typeof (TBoth).RequiresNone (); + typeof (TNothing).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void TestFields () + { + typeof (TFields).RequiresPublicFields (); + typeof (TFields).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + public static void TestMethods () + { + typeof (TMethods).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TMethods).RequiresNone (); + } + + public static void TestBoth () + { + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TBoth).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void TestNothing () + { + typeof (TNothing).RequiresPublicFields (); + typeof (TNothing).RequiresPublicMethods (); + typeof (TNothing).RequiresNone (); + } + } + + static void TestDeepNestedTypesWithGenerics () + { + RootTypeWithRequirements.InnerTypeWithNoAddedGenerics.TestAccess (); + } + + class RootTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TRoot> + { + public class InnerTypeWithNoAddedGenerics + { + [ExpectedWarning ("IL2087", nameof (TRoot), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.RootTypeWithRequirements", + "type", + "DataFlowTypeExtensions.RequiresPublicMethods(Type)")] + public static void TestAccess () + { + typeof (TRoot).RequiresPublicFields (); + typeof (TRoot).RequiresPublicMethods (); + } + } + } + + static void TestInterfaceTypeGenericRequirements () + { + IGenericInterfaceTypeWithRequirements instance = new InterfaceImplementationTypeWithInstantiatedGenericOnBase (); + new InterfaceImplementationTypeWithInstantiationOverSelfOnBase (); + new InterfaceImplementationTypeWithOpenGenericOnBase (); + new InterfaceImplementationTypeWithOpenGenericOnBaseWithRequirements (); + + RecursiveGenericWithInterfacesRequirement.Test (); + } + + interface IGenericInterfaceTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + } + + class InterfaceImplementationTypeWithInstantiatedGenericOnBase : IGenericInterfaceTypeWithRequirements + { + } + + interface IGenericInterfaceTypeWithRequiresAll<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> + { + } + + class InterfaceImplementationTypeWithInstantiationOverSelfOnBase : IGenericInterfaceTypeWithRequiresAll + { + } + + [ExpectedWarning ("IL2091", nameof (IGenericInterfaceTypeWithRequirements))] + class InterfaceImplementationTypeWithOpenGenericOnBase : IGenericInterfaceTypeWithRequirements + { + } + class InterfaceImplementationTypeWithOpenGenericOnBaseWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + : IGenericInterfaceTypeWithRequirements + { + } + + class RecursiveGenericWithInterfacesRequirement + { + interface IFace<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] T> + { + } + + class TestType : IFace + { + } + + public static void Test () + { + var a = typeof (IFace); + var t = new TestType (); + } + } + + static void TestTypeGenericRequirementsOnMembers () + { + // Basically just root everything we need to test + var instance = new TypeGenericRequirementsOnMembers (); + + _ = instance.PublicFieldsField; + _ = instance.PublicMethodsField; + + _ = instance.PublicFieldsProperty; + instance.PublicFieldsProperty = null; + _ = instance.PublicMethodsProperty; + instance.PublicMethodsProperty = null; + + instance.PublicFieldsMethodParameter (null); + instance.PublicMethodsMethodParameter (null); + + instance.PublicFieldsMethodReturnValue (); + instance.PublicMethodsMethodReturnValue (); + + instance.PublicFieldsMethodLocalVariable (); + instance.PublicMethodsMethodLocalVariable (); + } + + class TypeGenericRequirementsOnMembers<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + { + public TypeRequiresPublicFields PublicFieldsField; + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public TypeRequiresPublicMethods PublicMethodsField; + + public TypeRequiresPublicFields PublicFieldsProperty { + get; + set; + } + + [ExpectedWarning ("IL2091", nameof (TypeGenericRequirementsOnMembers), ProducedBy = ProducedBy.Analyzer)] + public TypeRequiresPublicMethods PublicMethodsProperty { + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + get => null; + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + set { } + } + + public void PublicFieldsMethodParameter (TypeRequiresPublicFields param) { } + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public void PublicMethodsMethodParameter (TypeRequiresPublicMethods param) { } + + public TypeRequiresPublicFields PublicFieldsMethodReturnValue () { return null; } + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public TypeRequiresPublicMethods PublicMethodsMethodReturnValue () { return null; } + + public void PublicFieldsMethodLocalVariable () + { + TypeRequiresPublicFields t = null; + } + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public void PublicMethodsMethodLocalVariable () + { + TypeRequiresPublicMethods t = null; + } + } + + static void TestPartialInstantiationTypes () + { + _ = new PartialyInstantiatedFields (); + _ = new FullyInstantiatedOverPartiallyInstantiatedFields (); + _ = new PartialyInstantiatedMethods (); + _ = new FullyInstantiatedOverPartiallyInstantiatedMethods (); + } + + class BaseForPartialInstantiation< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + } + + class PartialyInstantiatedFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseForPartialInstantiation + { + } + + class FullyInstantiatedOverPartiallyInstantiatedFields + : PartialyInstantiatedFields + { + } + + [ExpectedWarning ("IL2091", nameof (BaseForPartialInstantiation), "'TMethods'")] + class PartialyInstantiatedMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseForPartialInstantiation + { + // Analyzer does not see the base class constructor + [ExpectedWarning ("IL2091", nameof (BaseForPartialInstantiation), "'TMethods'", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public PartialyInstantiatedMethods () { } + } + + class FullyInstantiatedOverPartiallyInstantiatedMethods + : PartialyInstantiatedMethods + { + } + + static void TestSingleGenericParameterOnMethod () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + MethodRequiresPublicFieldsPassThrough (); + MethodRequiresNothingPassThrough (); + } + + /// + /// Adding a comment to verify that analyzer doesn't null ref when trying to analyze + /// generic parameter comments on a method + /// + /// + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void MethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [RequiresUnreferencedCode ("message")] + static void RUCMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicFields (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodRequiresNothing () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicMethods), "'T'")] + static void MethodRequiresPublicFieldsPassThrough< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + } + + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicFields), "'T'")] + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicMethods), "'T'")] + static void MethodRequiresNothingPassThrough () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + } + + static void TestMultipleGenericParametersOnMethod () + { + MethodMultipleWithDifferentRequirements_TestMultiple (); + MethodMultipleWithDifferentRequirements_TestFields (); + MethodMultipleWithDifferentRequirements_TestMethods (); + MethodMultipleWithDifferentRequirements_TestBoth (); + MethodMultipleWithDifferentRequirements_TestNothing (); + } + + static void MethodMultipleWithDifferentRequirements_TestMultiple< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TFields).RequiresPublicFields (); ; + typeof (TMethods).RequiresPublicMethods (); + typeof (TBoth).RequiresPublicFields (); ; + typeof (TBoth).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + typeof (TMethods).RequiresNone (); + typeof (TBoth).RequiresNone (); + typeof (TNothing).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodMultipleWithDifferentRequirements_TestFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TFields).RequiresPublicFields (); ; + typeof (TFields).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void MethodMultipleWithDifferentRequirements_TestMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TMethods).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TMethods).RequiresNone (); + } + + static void MethodMultipleWithDifferentRequirements_TestBoth< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TBoth).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodMultipleWithDifferentRequirements_TestNothing< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TNothing).RequiresPublicFields (); + typeof (TNothing).RequiresPublicMethods (); + typeof (TNothing).RequiresNone (); + } + + static void TestMethodGenericParametersViaInheritance () + { + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFields (); + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFieldsNonGeneric (); + + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticPartialInstantiation (); + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticPartialInstantiationUnrecognized (); + + var instance = new TypeWithInstantiatedGenericMethodViaGenericParameter (); + + instance.InstanceRequiresPublicFields (); + instance.InstanceRequiresPublicFieldsNonGeneric (); + + instance.VirtualRequiresPublicFields (); + instance.VirtualRequiresPublicMethods (); + + instance.CallInterface (); + + IInterfaceWithGenericMethod interfaceInstance = (IInterfaceWithGenericMethod) instance; + interfaceInstance.InterfaceRequiresPublicFields (); + interfaceInstance.InterfaceRequiresPublicMethods (); + } + + class BaseTypeWithGenericMethod + { + public static void StaticRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + public void InstanceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + public virtual void VirtualRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + + public static void StaticRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + public void InstanceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + public virtual void VirtualRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + + public static void StaticRequiresMultipleGenericParams< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> () + { + typeof (TFields).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + } + } + + interface IInterfaceWithGenericMethod + { + void InterfaceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> (); + void InterfaceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> (); + } + + + class TypeWithInstantiatedGenericMethodViaGenericParameter<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseTypeWithGenericMethod, IInterfaceWithGenericMethod + { + [ExpectedWarning ("IL2091", + "'TInner'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFields()", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresPublicMethods()")] + public static void StaticRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TInner> () + { + StaticRequiresPublicFields (); + StaticRequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresPublicMethods()")] + public static void StaticRequiresPublicFieldsNonGeneric () + { + StaticRequiresPublicFields (); + StaticRequiresPublicMethods (); + } + + public static void StaticPartialInstantiation () + { + StaticRequiresMultipleGenericParams (); + } + + [ExpectedWarning ("IL2091", + nameof (TOuter), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "TMethods", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'TMethods'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void StaticPartialInstantiationUnrecognized () + { + StaticRequiresMultipleGenericParams (); + } + + [ExpectedWarning ("IL2091", + "'TInner'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", "InstanceRequiresPublicFields", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.InstanceRequiresPublicMethods")] + public void InstanceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TInner> () + { + InstanceRequiresPublicFields (); + InstanceRequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.InstanceRequiresPublicMethods")] + public void InstanceRequiresPublicFieldsNonGeneric () + { + InstanceRequiresPublicFields (); + InstanceRequiresPublicMethods (); + } + + public override void VirtualRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); + } + + public override void VirtualRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicMethods (); + } + + public void InterfaceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); ; + } + + public void InterfaceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.IInterfaceWithGenericMethod.InterfaceRequiresPublicMethods")] + public void CallInterface () + { + IInterfaceWithGenericMethod interfaceInstance = (IInterfaceWithGenericMethod) this; + interfaceInstance.InterfaceRequiresPublicFields (); + interfaceInstance.InterfaceRequiresPublicMethods (); + } + } + + static void TestNewConstraintSatisfiesParameterlessConstructor () where T : new() + { + RequiresParameterlessConstructor (); + } + + static void TestStructConstraintSatisfiesParameterlessConstructor () where T : struct + { + RequiresParameterlessConstructor (); + } + static void TestUnmanagedConstraintSatisfiesParameterlessConstructor () where T : unmanaged + { + RequiresParameterlessConstructor (); + } + + static void RequiresParameterlessConstructor<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T> () + { + } + + // Warn about calls to static methods: + [ExpectedWarning ("IL2026", "TypeRequiresPublicFields", "RUCTest()", "message")] + [ExpectedWarning ("IL2026", "RUCMethodRequiresPublicMethods", "message")] + // And about type/method generic parameters on the RUC methods: + [ExpectedWarning ("IL2091", "TypeRequiresPublicFields")] + [ExpectedWarning ("IL2091", "RUCMethodRequiresPublicMethods")] + static void TestNoWarningsInRUCMethod () + { + TypeRequiresPublicFields.RUCTest (); + RUCMethodRequiresPublicMethods (); + } + + // Warn about calls to the static methods and the ctor on the RUC type: + [ExpectedWarning ("IL2026", "StaticMethod", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "RUCTypeRequiresPublicFields", "message")] + // And about method generic parameters: + [ExpectedWarning ("IL2091", "InstanceMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "StaticMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "StaticMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "VirtualMethodRequiresPublicMethods")] + // And about type generic parameters: (one for each reference to the type): + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethod + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // RUCTypeRequiresPublicFields ctor + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // RUCTypeRequiresPublicFields local, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // InstanceMethod, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // InstanceMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // VirtualMethod, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // VirtualMethodRequiresPublicMethods + static void TestNoWarningsInRUCType () + { + RUCTypeRequiresPublicFields.StaticMethod (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + var rucType = new RUCTypeRequiresPublicFields (); + rucType.InstanceMethod (); + rucType.InstanceMethodRequiresPublicMethods (); + rucType.VirtualMethod (); + rucType.VirtualMethodRequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("message")] + public class RUCTypeRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + public static void StaticMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public static void StaticMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + + public void InstanceMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public void InstanceMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + + public virtual void VirtualMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public virtual void VirtualMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + } + + static void TestGenericParameterFlowsToField () + { + TypeRequiresPublicFields.TestFields (); + } + + public class TestType + { + } + public struct TestStruct + { + } + + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs index 8aff7ec7ad2f7d..15381ba18b1b6c 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -45,7 +45,7 @@ class TypeInheritance { class BaseWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> { - public static void GetMethods() + public static void GetMethods () { typeof (TPublicMethods).GetMethods (); } @@ -91,7 +91,7 @@ class DerivedWithOnlyStaticMethodReference : BaseWithPublicMethods.GetMethods () // The type instantiation needs to be validated and in this case it produces a warning. - // This is no different from the same code being part of a completely unrelate method/class. + // This is no different from the same code being part of a completely unrelated method/class. // So the fact that this is in derived class has no impact on the validation in this case. [ExpectedWarning ("IL2091")] public static void GetDerivedMethods () => GetMethods (); @@ -417,6 +417,7 @@ static void MethodWithTwo< static MethodBody GetInstance () => null; + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // return type // NativeAOT_StorageSpaceType static TypeWithPublicMethods GetInstanceForTypeWithPublicMethods () => null; class TypeOf @@ -555,6 +556,7 @@ static void TwoMismatchesInOneStatement< } [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // local variable // NativeAOT_StorageSpaceType static void InstanceMethodMismatch () { TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); @@ -607,6 +609,7 @@ static void TwoMismatchesInOneStatement< // The local variable [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // access to the field // NativeAOT_StorageSpaceType static void InstanceFieldMismatch () { TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 51f2fa94c3f8c8..f7e5ee0286aa8e 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -3,14 +3,11 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Reflection.Metadata.Ecma335; using System.Text; using FluentAssertions; using ILCompiler; -using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Mono.Cecil; @@ -24,42 +21,6 @@ namespace Mono.Linker.Tests.TestCasesRunner { public class AssemblyChecker { - internal readonly struct AssemblyQualifiedToken : IEquatable - { - public string? AssemblyName { get; } - public int Token { get; } - - public AssemblyQualifiedToken (string? assemblyName, int token) => (AssemblyName, Token) = (assemblyName, token); - - public AssemblyQualifiedToken (TypeSystemEntity entity) => - (AssemblyName, Token) = entity switch { - EcmaType type => (type.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (type.Handle)), - EcmaMethod method => (method.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (method.Handle)), - PropertyPseudoDesc property => (((EcmaType) property.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (property.Handle)), - EventPseudoDesc @event => (((EcmaType) @event.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (@event.Handle)), - ILStubMethod => (null, 0), // Ignore compiler generated methods - _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {entity} yet.") - }; - - public AssemblyQualifiedToken (IMemberDefinition member) => - (AssemblyName, Token) = member switch { - TypeDefinition type => (type.Module.Assembly.Name.Name, type.MetadataToken.ToInt32 ()), - MethodDefinition method => (method.Module.Assembly.Name.Name, method.MetadataToken.ToInt32 ()), - PropertyDefinition property => (property.Module.Assembly.Name.Name, property.MetadataToken.ToInt32 ()), - EventDefinition @event => (@event.Module.Assembly.Name.Name, @event.MetadataToken.ToInt32 ()), - FieldDefinition field => (field.Module.Assembly.Name.Name, field.MetadataToken.ToInt32 ()), - _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {member} yet.") - }; - - public override int GetHashCode () => AssemblyName == null ? 0 : AssemblyName.GetHashCode () ^ Token.GetHashCode (); - public override string ToString () => $"{AssemblyName}: {Token}"; - public bool Equals (AssemblyQualifiedToken other) => - string.CompareOrdinal (AssemblyName, other.AssemblyName) == 0 && Token == other.Token; - public override bool Equals ([NotNullWhen (true)] object? obj) => ((AssemblyQualifiedToken?) obj)?.Equals (this) == true; - - public bool IsNil => AssemblyName == null; - } - private readonly BaseAssemblyResolver originalsResolver; private readonly ReaderParameters originalReaderParameters; private readonly AssemblyDefinition originalAssembly; diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs new file mode 100644 index 00000000000000..af39d8d687883c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs @@ -0,0 +1,51 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata.Ecma335; +using ILCompiler; +using Internal.IL.Stubs; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using Mono.Cecil; + +namespace Mono.Linker.Tests.TestCasesRunner +{ + internal readonly struct AssemblyQualifiedToken : IEquatable + { + public string? AssemblyName { get; } + public int Token { get; } + + public AssemblyQualifiedToken (string? assemblyName, int token) => (AssemblyName, Token) = (assemblyName, token); + + public AssemblyQualifiedToken (TypeSystemEntity entity) => + (AssemblyName, Token) = entity switch { + EcmaType type => (type.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (type.Handle)), + EcmaMethod method => (method.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (method.Handle)), + EcmaField field => (field.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (field.Handle)), + PropertyPseudoDesc property => (((EcmaType) property.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (property.Handle)), + EventPseudoDesc @event => (((EcmaType) @event.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (@event.Handle)), + ILStubMethod => (null, 0), // Ignore compiler generated methods + _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {entity} yet.") + }; + + public AssemblyQualifiedToken (IMemberDefinition member) => + (AssemblyName, Token) = member switch { + TypeDefinition type => (type.Module.Assembly.Name.Name, type.MetadataToken.ToInt32 ()), + MethodDefinition method => (method.Module.Assembly.Name.Name, method.MetadataToken.ToInt32 ()), + PropertyDefinition property => (property.Module.Assembly.Name.Name, property.MetadataToken.ToInt32 ()), + EventDefinition @event => (@event.Module.Assembly.Name.Name, @event.MetadataToken.ToInt32 ()), + FieldDefinition field => (field.Module.Assembly.Name.Name, field.MetadataToken.ToInt32 ()), + _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {member} yet.") + }; + + public override int GetHashCode () => AssemblyName == null ? 0 : AssemblyName.GetHashCode () ^ Token.GetHashCode (); + public override string ToString () => $"{AssemblyName}: {Token}"; + public bool Equals (AssemblyQualifiedToken other) => + string.CompareOrdinal (AssemblyName, other.AssemblyName) == 0 && Token == other.Token; + public override bool Equals ([NotNullWhen (true)] object? obj) => ((AssemblyQualifiedToken?) obj)?.Equals (this) == true; + + public bool IsNil => AssemblyName == null; + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 090a3a1b039b97..b0c0bdd4cfafb7 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -398,10 +398,17 @@ static bool LogMessageHasSameOriginMember (MessageContainer mc, ICustomAttribute { var origin = mc.Origin; Debug.Assert (origin != null); - if (NameUtils.GetActualOriginDisplayName (origin?.MemberDefinition) == NameUtils.GetExpectedOriginDisplayName (expectedOriginProvider)) + if (origin?.MemberDefinition == null) + return false; + if (expectedOriginProvider is not IMemberDefinition expectedOriginMember) + return false; + + var actualOriginToken = new AssemblyQualifiedToken (origin.Value.MemberDefinition); + var expectedOriginToken = new AssemblyQualifiedToken (expectedOriginMember); + if (actualOriginToken.Equals(expectedOriginToken)) return true; - var actualMember = origin!.Value.MemberDefinition; + var actualMember = origin.Value.MemberDefinition; // Compensate for cases where for some reason the OM doesn't preserve the declaring types // on certain things after trimming. if (actualMember != null && GetOwningType (actualMember) == null && From 39c5c2f34d2e79024cff94f2a4c4115064fdade4 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:24:17 -0800 Subject: [PATCH 4/5] PR feedback The biggest change is to rework how the type inheritance checks are triggered. The new solution adds a new dependency node with the generic type definition to the graph and then does analysis on that node. This is to avoid potential noise warnings which could happen due to multiple instantiations calling the checking code multiple times. With this the check is done only once on the type definition. Tweaked some tests to try to cover the multiple instances scenario. --- .../Dataflow/GenericArgumentDataFlow.cs | 18 +-- .../Compiler/Dataflow/MethodBodyScanner.cs | 12 +- .../Dataflow/ReflectionMethodBodyScanner.cs | 4 +- .../DataflowAnalyzedTypeDefinitionNode.cs | 109 ++++++++++++++++++ .../GenericDictionaryNode.cs | 2 - .../DependencyAnalysis/NodeFactory.cs | 13 +++ .../Compiler/MetadataManager.cs | 4 - .../Compiler/UsageBasedMetadataManager.cs | 27 +---- .../ILCompiler.Compiler.csproj | 1 + .../GenericParameterWarningLocation.cs | 21 +++- 10 files changed, 158 insertions(+), 53 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs index 6d7b357a9a6e18..284371f584789b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs @@ -41,25 +41,19 @@ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencie public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, TypeDesc type) { - if (type.HasInstantiation) + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (typeDefinition != type) { - TypeDesc typeDefinition = type.GetTypeDefinition(); - if (typeDefinition != type) - { - ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation); - } + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation); } } public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, MethodDesc method) { - if (method.HasInstantiation) + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (typicalMethod != method) { - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - if (typicalMethod != method) - { - ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation); - } + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation); } ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method.OwningType); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index f4beb9857e5927..1cf45fd45cb5b0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -1027,7 +1027,7 @@ protected void StoreInReference(MultiValue target, MultiValue source, MethodIL m StoreMethodLocalValue(locals, source, localReference.LocalIndex, curBasicBlock); break; case FieldReferenceValue fieldReference - when GetFieldValue(method, offset, fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: + when HandleGetField(method, offset, fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: HandleStoreField(method, offset, fieldValue, source); break; case ParameterReferenceValue parameterReference @@ -1059,7 +1059,7 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal } /// - /// GetFieldValue is called every time the scanner needs to represent a value of the field + /// HandleGetField is called every time the scanner needs to represent a value of the field /// either as a source or target. It is not called when just a reference to field is created, /// But if such reference is dereferenced then it will get called. /// It is NOT called for hoisted locals. @@ -1076,7 +1076,7 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal /// need to track those. It makes the design a bit cleaner because hoisted locals are purely handled in here /// and don't leak over to the reflection handling code in any way. /// - protected abstract MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field); + protected abstract MultiValue HandleGetField(MethodIL methodBody, int offset, FieldDesc field); private void ScanLdfld( MethodIL methodBody, @@ -1102,7 +1102,7 @@ private void ScanLdfld( } else { - value = GetFieldValue(methodBody, offset, field); + value = HandleGetField(methodBody, offset, field); } currentStack.Push(new StackSlot(value)); } @@ -1138,7 +1138,7 @@ private void ScanStfld( return; } - foreach (var value in GetFieldValue(methodBody, offset, field)) + foreach (var value in HandleGetField(methodBody, offset, field)) { // GetFieldValue may return different node types, in which case they can't be stored to. // At least not yet. @@ -1199,7 +1199,7 @@ internal MultiValue DereferenceValue( dereferencedValue, CompilerGeneratedState.IsHoistedLocal(fieldReferenceValue.FieldDefinition) ? interproceduralState.GetHoistedLocal(new HoistedLocalKey(fieldReferenceValue.FieldDefinition)) - : GetFieldValue(methodBody, offset, fieldReferenceValue.FieldDefinition)); + : HandleGetField(methodBody, offset, fieldReferenceValue.FieldDefinition)); break; case ParameterReferenceValue parameterReferenceValue: dereferencedValue = MultiValue.Meet( 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 8b54be44762117..7db537975b2201 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -140,11 +140,11 @@ private MethodParameterValue GetMethodParameterValue(ParameterProxy parameter, D => _annotations.GetMethodParameterValue(parameter, dynamicallyAccessedMemberTypes); /// - /// GetFieldValue is called every time the scanner needs to represent a value of the field + /// HandleGetField is called every time the scanner needs to represent a value of the field /// either as a source or target. It is not called when just a reference to field is created, /// But if such reference is dereferenced then it will get called. /// - protected override MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field) + protected override MultiValue HandleGetField(MethodIL methodBody, int offset, FieldDesc field) { _origin = _origin.WithInstructionOffset(methodBody, offset); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs new file mode 100644 index 00000000000000..59dd3ec251ab60 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; + +using ILCompiler.Dataflow; +using ILCompiler.DependencyAnalysisFramework; + +using ILLink.Shared.TrimAnalysis; + +namespace ILCompiler.DependencyAnalysis +{ + public class DataflowAnalyzedTypeDefinitionNode : DependencyNodeCore + { + private readonly TypeDesc _typeDefinition; + + public DataflowAnalyzedTypeDefinitionNode(TypeDesc typeDefinition) + { + Debug.Assert(typeDefinition.IsTypeDefinition); + _typeDefinition = typeDefinition; + } + + public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, FlowAnnotations flowAnnotations, TypeDesc type) + { + bool foundGenericParameterAnnotation = false; + + type = type.GetTypeDefinition(); + + try + { + if (type.HasBaseType) + { + foundGenericParameterAnnotation |= IsTypeWithGenericParameterAnnotations(flowAnnotations, type.BaseType); + } + + if (type is MetadataType metadataType) + { + foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces) + { + foundGenericParameterAnnotation |= IsTypeWithGenericParameterAnnotations(flowAnnotations, interfaceType); + } + } + } + catch (TypeSystemException) + { + // Wasn't able to do dataflow because of missing references or something like that. + // This likely won't compile either, so we don't care about missing dependencies. + } + + if (foundGenericParameterAnnotation) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.DataflowAnalyzedTypeDefinition(type), "Generic parameter dataflow"); + } + + static bool IsTypeWithGenericParameterAnnotations(FlowAnnotations flowAnnotations, TypeDesc type) + => type.HasInstantiation && flowAnnotations.HasGenericParameterAnnotation(type); + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + + DependencyList dependencies = null; + + if (_typeDefinition.HasBaseType) + { + GetDataFlowDependenciesForInstantiation(ref dependencies, mdManager.Logger, factory, mdManager.FlowAnnotations, _typeDefinition.BaseType, _typeDefinition); + } + + if (_typeDefinition is MetadataType metadataType) + { + foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces) + { + GetDataFlowDependenciesForInstantiation(ref dependencies, mdManager.Logger, factory, mdManager.FlowAnnotations, interfaceType, _typeDefinition); + } + } + + return dependencies; + } + + private static void GetDataFlowDependenciesForInstantiation( + ref DependencyList dependencies, + Logger logger, + NodeFactory factory, + FlowAnnotations flowAnnotations, + TypeDesc type, + TypeDesc contextType) + { + TypeDesc instantiatedType = type.InstantiateSignature(contextType.Instantiation, Instantiation.Empty); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, logger, factory, flowAnnotations, new Logging.MessageOrigin(contextType), instantiatedType); + } + + protected override string GetName(NodeFactory factory) + { + return "Dataflow analysis for type definition " + _typeDefinition.ToString(); + } + + 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 context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index c6c63fa6498517..bcd3797984d207 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -142,8 +142,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } } - factory.MetadataManager.GetDependenciesForGenericDictionary(ref result, factory, _owningType); - return result; } 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 196a51ea2c31f5..69cdd10cefdc58 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -378,6 +378,11 @@ private void CreateNodeCaches() return new DataflowAnalyzedMethodNode(il.MethodIL); }); + _dataflowAnalyzedTypeDefinitions = new NodeCache((TypeDesc type) => + { + return new DataflowAnalyzedTypeDefinitionNode(type); + }); + _dynamicDependencyAttributesOnEntities = new NodeCache((TypeSystemEntity entity) => { return new DynamicDependencyAttributesOnEntityNode(entity); @@ -704,6 +709,14 @@ public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL) return _dataflowAnalyzedMethods.GetOrAdd(new MethodILKey(methodIL)); } + private NodeCache _dataflowAnalyzedTypeDefinitions; + + public DataflowAnalyzedTypeDefinitionNode DataflowAnalyzedTypeDefinition(TypeDesc type) + { + Debug.Assert(type.IsTypeDefinition); + return _dataflowAnalyzedTypeDefinitions.GetOrAdd(type); + } + private NodeCache _dynamicDependencyAttributesOnEntities; public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEntity(TypeSystemEntity entity) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 680d2b34dfeb82..dc7761b6122bda 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -897,10 +897,6 @@ public virtual void GetDependenciesForGenericDictionary(ref DependencyList depen { } - public virtual void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - } - public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) { } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 90efd3aa6a5070..79c100068bad9d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -407,32 +407,7 @@ public override void GetDependenciesDueToEETypePresence(ref DependencyList depen { base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); - try - { - if (type.HasBaseType) - { - GetDataFlowDependenciesForInstantiation(ref dependencies, factory, type.BaseType, type); - } - - foreach (var runtimeInterface in type.RuntimeInterfaces) - { - GetDataFlowDependenciesForInstantiation(ref dependencies, factory, runtimeInterface, type); - } - } - catch (TypeSystemException) - { - // Wasn't able to do dataflow because of missing references or something like that. - // This likely won't compile either, so we don't care about missing dependencies. - } - } - - private void GetDataFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, TypeDesc contextType) - { - if (type.HasInstantiation && FlowAnnotations.HasGenericParameterAnnotation(type)) - { - TypeDesc instantiatedType = type.InstantiateSignature(contextType.Instantiation, Instantiation.Empty); - GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, Logger, factory, FlowAnnotations, new Logging.MessageOrigin(contextType), instantiatedType); - } + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); } public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 289d7d0b5325a4..15955617c20077 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -387,6 +387,7 @@ + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs index 15381ba18b1b6c..308c7dfa211052 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -67,7 +67,10 @@ class DerivedWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAcce [ExpectedWarning ("IL2091")] class DerivedWithNoAnnotations : BaseWithPublicMethods - { } + { + [ExpectedWarning ("IL2091")] // Compiler generates an implicit call to BaseWithPublicMethods..ctor + public DerivedWithNoAnnotations () { } + } [ExpectedWarning ("IL2091")] class DerivedWithMismatchAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> @@ -97,6 +100,14 @@ class DerivedWithOnlyStaticMethodReference : BaseWithPublicMethods GetMethods (); } + [ExpectedWarning ("IL2091")] + static void TestWithUnannotatedTypeArgument () + { + object a; + a = new DerivedWithMatchingAnnotation (); // IL2091 due to the instantiation + a = new DerivedWithNoAnnotations (); + } + public static void Test () { Type t; @@ -107,6 +118,14 @@ public static void Test () t = typeof (DerivedWithOneMismatch<>); t = typeof (DerivedWithTwoMatching<,>); + // Also try exact instantiations + object a; + a = new DerivedWithMatchingAnnotation (); + a = new DerivedWithMatchingAnnotation (); + + // Also try with unannotated type parameter + TestWithUnannotatedTypeArgument (); + DerivedWithOnlyStaticMethodReference.GetDerivedMethods (); } } From 440049bded988836d92b61b32ed33a606c7061cc Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 25 Jan 2023 02:46:04 -0800 Subject: [PATCH 5/5] PR feedback --- .../Compiler/DependencyAnalysis/NodeFactory.cs | 1 - src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs | 2 +- .../DataFlow/GenericParameterDataFlow.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) 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 69cdd10cefdc58..5fac5d9cbc4bfb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -713,7 +713,6 @@ public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL) public DataflowAnalyzedTypeDefinitionNode DataflowAnalyzedTypeDefinition(TypeDesc type) { - Debug.Assert(type.IsTypeDefinition); return _dataflowAnalyzedTypeDefinitions.GetOrAdd(type); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs index 4a3a9312e3747c..a27f06c3aa1be3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs @@ -147,7 +147,7 @@ public void LogError(TypeSystemEntity origin, DiagnosticId id, params string[] a internal bool IsWarningSuppressed(int code, MessageOrigin origin) { // This is causing too much noise - // https://github.com/dotnet/runtimelab/issues/1591 + // https://github.com/dotnet/runtime/issues/81156 if (code == 2110 || code == 2111 || code == 2113 || code == 2115) return true; diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index aa80500a606f4c..dace9d177887c1 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -209,7 +209,7 @@ static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnBase () new DerivedTypeWithOpenGenericOnBaseWithRUCOnBase (); } - // TODO !!! + // https://github.com/dotnet/runtime/issues/81158 [ExpectedWarning ("IL2109", nameof (BaseTypeWithOpenGenericDAMTAndRUC), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] [ExpectedWarning ("IL2091", nameof (BaseTypeWithOpenGenericDAMTAndRUC))] class DerivedTypeWithOpenGenericOnBaseWithRUCOnBase : BaseTypeWithOpenGenericDAMTAndRUC