Fix custom-attribute generic argument dataflow in the trimmer#128776
Fix custom-attribute generic argument dataflow in the trimmer#128776Copilot wants to merge 8 commits into
Conversation
Co-authored-by: jtschuster <36744439+jtschuster@users.noreply.github.com>
…new()-constrained ctor Co-authored-by: jtschuster <36744439+jtschuster@users.noreply.github.com>
|
Tagging subscribers to this area: @agocke, @dotnet/illink |
There was a problem hiding this comment.
Pull request overview
This PR updates ILLink’s marking behavior for custom attributes so generic instantiations on attribute types participate in generic argument dataflow, ensuring requirements like new() constraints (and related annotations) are honored when attributes are materialized via reflection.
Changes:
- Run
GenericArgumentDataFlow.ProcessGenericArgumentDataFlowfor the attribute constructor’s declaring type duringMarkStep.MarkCustomAttribute. - Add a regression test that reflects over a type with a
new()-constrained generic attribute and asserts the generic argument’s public parameterless constructor is preserved.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/tools/illink/src/linker/Linker.Steps/MarkStep.cs | Invokes generic-argument dataflow for attribute type instantiations when marking custom attributes. |
| src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/NewConstrainedGenericAttributeArgumentConstructorIsKept.cs | Adds a linker test covering new()-constrained generic attribute arguments accessed via reflection. |
| using System; | ||
| using System.Reflection; | ||
| using Mono.Linker.Tests.Cases.Expectations.Assertions; |
| GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(in origin, this, Context, constructor_type); | ||
| TypeReference constructor_type = ca.Constructor.DeclaringType; | ||
| TypeDefinition? type = Context.Resolve(constructor_type); |
|
|
||
| [Kept] | ||
| [KeptBaseType(typeof(Attribute))] | ||
| class MyAttribute<[KeptGenericParamAttributes(GenericParameterAttributes.DefaultConstructorConstraint)] T> : Attribute where T : new() |
There was a problem hiding this comment.
@copilot Can we move this to the existing GenericAttributes tests? And could we add a test for a generic attribute where the generic parameter has DynamicallyAccessedMembers?
There was a problem hiding this comment.
@copilot please move this test case to GenericAttributes tests and add a similar test case where the generic parameter has DynamicallyAccessedMembers. It should warn when the T passed in is unannotated, and not warn when it is annotated. It should look similar to below.
typeof(GenericClass<int, string>.InnerClass).GetCustomAttributes();
typeof(GenericClass<int, string>.InnerClass2).GetCustomAttributes();
class MyAnnotatedAttribute<[DynamicallyAccessedMembers(DAMT.Methods)] T>
{
}
class GenericClass<TUnnotated, [DynamicallyAccessedMembers(DAMT.Methods)]TAnnotated>
{
[MyAnnotated<TUnannotated>] // Warns
public class InnerClass { }
[MyAnnotated<TAnnotated>] // no warning
public class InnerClass2 { }
}| TypeReference constructor_type = ca.Constructor.DeclaringType; | ||
| GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(in origin, this, Context, constructor_type); | ||
|
|
Co-authored-by: jtschuster <36744439+jtschuster@users.noreply.github.com>
The ILLink trimmer removed the public parameterless constructor of a type used only as the generic argument of a
new()-constrained generic attribute reached via reflection, leaving thenew()constraint unsatisfiable and causing aTypeLoadExceptionatGetCustomAttributes. No trim warning was emitted.Root cause
#119419 moved new()-constraint default-ctor marking out of
MarkStep.MarkGenericArguments(which ran for every generic instantiation) intoGenericArgumentDataFlow.ProcessGenericArgumentDataFlow. That dataflow path is only invoked for reflection-visible methods, fields, and interfaces — never for custom attribute generic instantiations — so the requirement was silently dropped for types like[MyAttribute<Handler>].Changes
MarkStep.MarkCustomAttribute: run generic-argument dataflow on the attribute constructor's declaring type so custom attribute generic instantiations participate in the samenew()-constraint andDynamicallyAccessedMembersprocessing as other reflection-visible generic usages.GenericArgumentDataFlow.ProcessGenericArgumentDataFlowwhen the attribute type actually requires generic-argument dataflow, avoiding the extra diagnostic/reflection-marker setup for non-generic attributes.Attributes/GenericAttributestest instead of a standalone test file, keeping thenew()-constraint repro there and asserting thatHandler::.ctor()is preserved when reflecting on[MyAttribute<Handler>].DynamicallyAccessedMembers, covering the warning path when the supplied type argument is unannotated and the no-warning path when it is annotated.The fix remains scoped to ILLink and avoids reverting #119419's intended dataflow/warning behavior while extending coverage to the related custom-attribute
DynamicallyAccessedMemberscase.