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..a6b0e5f9f7a8e0 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,83 @@
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, NodeFactory factory, in MessageOrigin origin, TypeDesc type, TypeDesc contextType)
+ {
+ ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextType.Instantiation, Instantiation.Empty);
+ }
- public GenericArgumentDataFlow(Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin)
+ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, MethodDesc contextMethod)
{
- _logger = logger;
- _factory = factory;
- _annotations = annotations;
- _origin = origin;
+ ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextMethod.OwningType.Instantiation, contextMethod.Instantiation);
}
- public DependencyList ProcessGenericArgumentDataFlow(GenericParameterDesc genericParameter, TypeDesc genericArgument)
+ private static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, Instantiation typeContext, Instantiation methodContext)
{
- var genericParameterValue = _annotations.GetGenericParameterValue(genericParameter);
- Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None);
+ if (!type.HasInstantiation)
+ return;
- MultiValue genericArgumentValue = _annotations.GetTypeValueFromGenericArgument(genericArgument);
+ TypeDesc instantiatedType = type.InstantiateSignature(typeContext, methodContext);
+
+ var mdManager = (UsageBasedMetadataManager)factory.MetadataManager;
var diagnosticContext = new DiagnosticContext(
- _origin,
- _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
- _logger);
- return RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue, genericParameter.GetDisplayName());
+ origin,
+ !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, instantiatedType);
+
+ if (reflectionMarker.Dependencies.Count > 0)
+ {
+ if (dependencies == null)
+ dependencies = reflectionMarker.Dependencies;
+ else
+ dependencies.AddRange(reflectionMarker.Dependencies);
+ }
+ }
+
+ public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, TypeDesc type)
+ {
+ TypeDesc typeDefinition = type.GetTypeDefinition();
+ if (typeDefinition != type)
+ {
+ ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation);
+ }
+ }
+
+ public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, MethodDesc method)
+ {
+ MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
+ if (typicalMethod != method)
+ {
+ ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation);
+ }
+
+ ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method.OwningType);
+ }
+
+ 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..b35018628c3bfd
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs
@@ -0,0 +1,96 @@
+// 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;
+using ILCompiler.Logging;
+
+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)
+ {
+ DependencyList dependencies = null;
+
+ if (_typeDefinition.HasBaseType)
+ {
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), _typeDefinition.BaseType, _typeDefinition);
+ }
+
+ if (_typeDefinition is MetadataType metadataType)
+ {
+ foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces)
+ {
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), interfaceType, _typeDefinition);
+ }
+ }
+
+ return dependencies;
+ }
+
+ 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/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/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/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/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