From e3a62da90f753e89c904340b72b1384f589d3422 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 1 Feb 2023 03:47:35 -0800 Subject: [PATCH 1/2] Revert "Revert "Fix generic parameter data flow validation in NativeAOT (#80956)" (#81259)" This reverts commit faa19f21bfe48aa2e2091a5b3010ac0016a34d5e. --- .../Common/Compiler/DisplayNameHelpers.cs | 5 +- .../Compiler/Dataflow/FlowAnnotations.cs | 34 +- .../Dataflow/GenericArgumentDataFlow.cs | 77 +- .../Compiler/Dataflow/MethodBodyScanner.cs | 57 +- .../Dataflow/ReflectionMethodBodyScanner.cs | 69 +- ...alysisGenericInstantiationAccessPattern.cs | 51 + .../Dataflow/TrimAnalysisPatternStore.cs | 17 +- .../TrimAnalysisReflectionAccessPattern.cs | 20 +- .../ConstructedEETypeNode.cs | 4 +- .../DataflowAnalyzedTypeDefinitionNode.cs | 109 ++ .../Compiler/DependencyAnalysis/EETypeNode.cs | 4 +- .../GenericDefinitionEETypeNode.cs | 4 +- .../GenericDictionaryNode.cs | 2 - .../DependencyAnalysis/NodeFactory.cs | 12 + .../ILCompiler.Compiler/Compiler/Logger.cs | 2 +- .../Compiler/MetadataManager.cs | 6 +- .../Compiler/UsageBasedMetadataManager.cs | 80 +- .../ILCompiler.Compiler.csproj | 2 + .../DataFlow/GenericParameterDataFlow.cs | 892 ++++++++++++ .../GenericParameterWarningLocation.cs | 1267 +++++++++++++++++ .../TestCasesRunner/AssemblyChecker.cs | 39 - .../TestCasesRunner/AssemblyQualifiedToken.cs | 51 + .../TestCasesRunner/ResultChecker.cs | 11 +- 23 files changed, 2637 insertions(+), 178 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.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 e724d905a5053e..5804b1b036919f 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..284371f584789b 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,66 @@ 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, typeHierarchyDataFlowOrigin: null, 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; + 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); + 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, genericParameter.GetDisplayName()); + 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 b9d09dbc87fbe7..fa83eca4f903cf 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 HandleGetField(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); + /// + /// 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. + /// + /// + /// 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 HandleGetField(MethodIL methodBody, int offset, FieldDesc field); private void ScanLdfld( MethodIL methodBody, @@ -1083,7 +1102,7 @@ private void ScanLdfld( } else { - value = GetFieldValue(field); + value = HandleGetField(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 HandleGetField(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)); + : HandleGetField(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..7db537975b2201 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); + /// + /// 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 HandleGetField(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) { @@ -160,16 +172,33 @@ 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 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) @@ -201,6 +230,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 +665,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..8f40b94c02d811 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,12 @@ // 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 Internal.TypeSystem; +#nullable enable + namespace ILCompiler.Dataflow { public readonly record struct TrimAnalysisReflectionAccessPattern @@ -17,12 +20,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); + break; + + case FieldDesc field: + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field); + 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/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/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 6f9aebfd9722e9..781a633c1aa78f 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/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 ba0f5be86ea0f6..37400c714162f7 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); @@ -709,6 +714,13 @@ public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL) return _dataflowAnalyzedMethods.GetOrAdd(new MethodILKey(methodIL)); } + private NodeCache _dataflowAnalyzedTypeDefinitions; + + public DataflowAnalyzedTypeDefinitionNode DataflowAnalyzedTypeDefinition(TypeDesc type) + { + return _dataflowAnalyzedTypeDefinitions.GetOrAdd(type); + } + private NodeCache _dynamicDependencyAttributesOnEntities; public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEntity(TypeSystemEntity entity) 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/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 19d8a8a0f99a29..f848eb3496b1fb 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); @@ -907,10 +907,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 efd659f42cd1c5..bc63153f1228ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -403,6 +403,13 @@ 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); + + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); + } + public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) { // Note: these are duplicated with the checks in GetConditionalDependenciesDueToEETypePresence @@ -575,27 +582,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 +766,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 +775,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 db2e9308dc0a6b..3e9d28a7a2d801 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 @@ + @@ -386,6 +387,7 @@ + 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..dace9d177887c1 --- /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 (); + } + + // https://github.com/dotnet/runtime/issues/81158 + [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 new file mode 100644 index 00000000000000..308c7dfa211052 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -0,0 +1,1267 @@ +// 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")] // Compiler generates an implicit call to BaseWithPublicMethods..ctor + public DerivedWithNoAnnotations () { } + } + + [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 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 (); + } + + [ExpectedWarning ("IL2091")] + static void TestWithUnannotatedTypeArgument () + { + object a; + a = new DerivedWithMatchingAnnotation (); // IL2091 due to the instantiation + a = new DerivedWithNoAnnotations (); + } + + public static void Test () + { + Type t; + t = typeof (DerivedWithSpecificType); + t = typeof (DerivedWithMatchingAnnotation<>); + t = typeof (DerivedWithNoAnnotations<>); + t = typeof (DerivedWithMismatchAnnotation<>); + 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 (); + } + } + + 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; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // return type // NativeAOT_StorageSpaceType + 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 + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // local variable // 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 + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // access to the field // 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 () { } + } +} 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 31039ca03779203bced6f2605051dfd9d50456f0 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 2 Feb 2023 02:01:51 -0800 Subject: [PATCH 2/2] Implement logic which compensates for a wrong suppression in DependencyInjection library See https://github.com/dotnet/runtime/issues/81358 for details. Functional change: When method or field are reflectable (we produce metadata for them) also go over all of their signature types and perform generic parameter data flow on them. Added tests for these cases into the data flow suite. Added a smoke test which is a simplified version of the DI scenario which was broken. --- .../Dataflow/GenericArgumentDataFlow.cs | 27 +++- .../DataflowAnalyzedTypeDefinitionNode.cs | 19 +-- .../DependencyAnalysis/FieldMetadataNode.cs | 6 + .../DependencyAnalysis/MethodMetadataNode.cs | 11 ++ .../GenericParameterWarningLocation.cs | 137 ++++++++++++++++ .../DependencyInjectionPattern.cs | 146 ++++++++++++++++++ .../SmokeTests/TrimmingBehaviors/Main.cs | 1 + .../TrimmingBehaviors.csproj | 1 + 8 files changed, 327 insertions(+), 21 deletions(-) create mode 100644 src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.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 284371f584789b..a6b0e5f9f7a8e0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs @@ -20,15 +20,32 @@ namespace ILCompiler.Dataflow { public static class GenericArgumentDataFlow { - public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin, TypeDesc type) + public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, TypeDesc contextType) { + ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextType.Instantiation, Instantiation.Empty); + } + + public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, MethodDesc contextMethod) + { + ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextMethod.OwningType.Instantiation, contextMethod.Instantiation); + } + + private static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, Instantiation typeContext, Instantiation methodContext) + { + if (!type.HasInstantiation) + return; + + TypeDesc instantiatedType = type.InstantiateSignature(typeContext, methodContext); + + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; + var diagnosticContext = new DiagnosticContext( origin, - !logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), - logger); - var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlowOrigin: null, enabled: true); + !mdManager.Logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), + mdManager.Logger); + var reflectionMarker = new ReflectionMarker(mdManager.Logger, factory, mdManager.FlowAnnotations, typeHierarchyDataFlowOrigin: null, enabled: true); - ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type); + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, instantiatedType); if (reflectionMarker.Dependencies.Count > 0) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs index 59dd3ec251ab60..b35018628c3bfd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -10,6 +10,7 @@ using ILCompiler.DependencyAnalysisFramework; using ILLink.Shared.TrimAnalysis; +using ILCompiler.Logging; namespace ILCompiler.DependencyAnalysis { @@ -62,38 +63,24 @@ static bool IsTypeWithGenericParameterAnnotations(FlowAnnotations flowAnnotation 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); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), _typeDefinition.BaseType, _typeDefinition); } if (_typeDefinition is MetadataType metadataType) { foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces) { - GetDataFlowDependenciesForInstantiation(ref dependencies, mdManager.Logger, factory, mdManager.FlowAnnotations, interfaceType, _typeDefinition); + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), 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(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs index 3c98314c9d9d37..325a7bb7946276 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; +using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Logging; using Internal.TypeSystem; @@ -43,6 +45,10 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (_field is EcmaField ecmaField) { DynamicDependencyAttributesOnEntityNode.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaField); + + // On a reflectable field, perform generic data flow for the field's type + // This is a compensation for the DI issue described in https://github.com/dotnet/runtime/issues/81358 + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_field), ecmaField.FieldType, ecmaField.OwningType); } return dependencies; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs index f52615dc369951..d29fefdccafaa0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; +using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Logging; using Internal.TypeSystem; @@ -51,6 +53,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (_method is EcmaMethod ecmaMethod) { DynamicDependencyAttributesOnEntityNode.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaMethod); + + // On a reflectable method, perform generic data flow for the return type and all the parameter types + // This is a compensation for the DI issue described in https://github.com/dotnet/runtime/issues/81358 + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_method), _method.Signature.ReturnType, _method); + + foreach (TypeDesc parameterType in _method.Signature) + { + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_method), parameterType, _method); + } } return dependencies; 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 308c7dfa211052..b3f81f94531b18 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 @@ -6,6 +6,7 @@ using System.Linq.Expressions; using System.Security.Policy; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; namespace Mono.Linker.Tests.Cases.DataFlow { @@ -36,9 +37,12 @@ public static void Main () TypeInheritance.Test (); TypeImplementingInterface.Test (); MethodParametersAndReturn.Test (); + MethodParametersAndReturnAccessedViaReflection.Test (); FieldDefinition.Test (); + FieldDefinitionViaReflection.Test (); PropertyDefinition.Test (); MethodBody.Test (); + GenericAttributes.Test (); } class TypeInheritance @@ -211,6 +215,12 @@ static IWithTwo MethodWithTwoMismatchesInReturn< [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null; + class ConstructorWithOneMatchAndOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public ConstructorWithOneMatchAndOneMismatch (IWithTwo two) { } + } + public static void Test () { MethodWithSpecificType (null, null); @@ -221,6 +231,59 @@ public static void Test () MethodWithMatchingReturn (); MethodWithOneMismatchReturn (); MethodWithTwoMismatchesInReturn (); + + _ = new ConstructorWithOneMatchAndOneMismatch (null); + } + } + + class MethodParametersAndReturnAccessedViaReflection + { + 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")] + static void MethodWithOneMismatch (TypeWithPublicMethods one) { } + + [ExpectedWarning ("IL2091", nameof (IWithTwo))] + [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods))] + 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")] + static TypeWithPublicMethods MethodWithOneMismatchReturn () => null; + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo MethodWithTwoMismatchesInReturn< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () => null; + + class ConstructorWithOneMatchAndOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + [ExpectedWarning ("IL2091")] + public ConstructorWithOneMatchAndOneMismatch (IWithTwo two) { } + } + + public static void Test () + { + // Access all of the methods via reflection + typeof (MethodParametersAndReturnAccessedViaReflection).RequiresNonPublicMethods (); + typeof (ConstructorWithOneMatchAndOneMismatch<>).RequiresPublicConstructors (); } } @@ -295,6 +358,53 @@ public static void Test () } } + class FieldDefinitionViaReflection + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods _field; + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods _field; + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field1; + static TypeWithPublicMethods _field2; + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field3; + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo _field; + } + + public static void Test () + { + typeof (SpecificType).RequiresNonPublicFields (); + typeof (OneMatchingAnnotation<>).RequiresNonPublicFields (); + typeof (MultipleReferencesToTheSameType<,>).RequiresNonPublicFields (); + typeof (TwoMismatchesInOne<,>).RequiresNonPublicFields (); + } + } + //.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 @@ -1260,6 +1370,33 @@ public static void Test () } } + // There are no warnings due to data flow itself + // since the generic attributes must be fully instantiated always. + class GenericAttributes + { + class TypeWithPublicMethodsAttribute<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : Attribute + { } + + class TypeWithTwoAttribute< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : Attribute + { } + + [TypeWithPublicMethods] + static void OneSpecificType () { } + + [TypeWithTwo] + static void TwoSpecificTypes () { } + + public static void Test () + { + OneSpecificType (); + TwoSpecificTypes (); + } + } + class TestType { } static void DoNothing () { } diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs new file mode 100644 index 00000000000000..de13a832348170 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +public class DependencyInjectionPattern +{ + public static int Run() + { + Services services = new(); + services.RegisterService(typeof(INameProvider<>), typeof(NameProviderService<>)); + services.RegisterService(typeof(IDataObjectPrinter), typeof(DataObjectPrinterService)); + + var printer = services.GetService(); + var actual = printer.GetNameLength(new DataObject() { Name = "0123456789" }); + Assert.Equal(10, actual); + + return 100; + } +} + +public class DataObject +{ + public string Name { get; set; } +} + +// Simplistic implementation of DI which is comparable in behavior to our DI +class Services +{ + private Dictionary _services = new Dictionary(); + + public void RegisterService(Type interfaceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType) + { + _services.Add(interfaceType, implementationType); + } + + public T GetService() + { + return (T)GetService(typeof(T)); + } + + public object GetService(Type interfaceType) + { + Type typeDef = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType; + Type implementationType = GetImplementationType(typeDef); + + if (implementationType.IsGenericTypeDefinition) + { + for (int i = 0; i < implementationType.GetGenericArguments().Length; i++) + { + Type genericArgument = implementationType.GetGenericArguments()[i]; + Type genericParameter = interfaceType.GetGenericArguments()[i]; + + // Validate that DAM annotations match + if (!DamAnnotationsMatch(genericArgument, genericParameter)) + throw new InvalidOperationException(); + + if (genericParameter.IsValueType) + throw new InvalidOperationException(); + } + + implementationType = InstantiateServiceType(implementationType, interfaceType.GetGenericArguments()); + } + + ConstructorInfo constructor = implementationType.GetConstructors()[0]; // Simplification + if (constructor.GetParameters().Length > 0) + { + List instances = new(); + foreach (var parameter in constructor.GetParameters()) + { + instances.Add(GetService(parameter.ParameterType)); + } + + return Activator.CreateInstance(implementationType, instances.ToArray())!; + } + else + { + return Activator.CreateInstance(implementationType)!; + } + + [UnconditionalSuppressMessage("", "IL2068", Justification = "We only add types with the right annotation to the dictionary")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type GetImplementationType(Type interfaceType) + { + if (!_services.TryGetValue(interfaceType, out Type? implementationType)) + throw new NotImplementedException(); + + return implementationType; + } + + [UnconditionalSuppressMessage("", "IL2055", Justification = "We validated that the type parameters match - THIS IS WRONG")] + [UnconditionalSuppressMessage("", "IL3050", Justification = "We validated there are no value types")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type InstantiateServiceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeDef, Type[] typeParameters) + { + return typeDef.MakeGenericType(typeParameters); + } + } + + private bool DamAnnotationsMatch(Type argument, Type parameter) + { + // .... - not interesting for this test, it will be true in the cases we use in this test + return true; + } +} + +interface INameProvider<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> +{ + string? GetName(T instance); +} + +class NameProviderService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> + : INameProvider +{ + public string? GetName(T instance) + { + return (string?)typeof(T).GetProperty("Name")?.GetValue(instance); + } +} + +interface IDataObjectPrinter +{ + int GetNameLength(DataObject instance); +} + +class DataObjectPrinterService : IDataObjectPrinter +{ + // The data flow is not applied on the INameProvider here, or in the method parameter + // or in the call to the GetName below inside Print. + INameProvider _nameProvider; + + public DataObjectPrinterService(INameProvider nameProvider) + { + _nameProvider = nameProvider; + } + + public int GetNameLength(DataObject instance) + { + // This throws because DataObject.Name is not preserved + string? name = _nameProvider.GetName(instance); + return name == null ? 0 : name.Length; + } +} diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs index e2c96e8cc00292..7a46f41961da19 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs @@ -8,6 +8,7 @@ success &= RunTest(DeadCodeElimination.Run); success &= RunTest(FeatureSwitches.Run); success &= RunTest(ILLinkDescriptor.Run); +success &= RunTest(DependencyInjectionPattern.Run); return success ? 100 : 1; diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj index d324f0ab841ad0..9caa415a7762a9 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj @@ -11,6 +11,7 @@ +