Support typeof(T).Assembly.GetType(string) in ILLink dataflow analysis#127319
Support typeof(T).Assembly.GetType(string) in ILLink dataflow analysis#127319MichalStrehovsky wants to merge 7 commits intodotnet:mainfrom
Conversation
Teach the trimmer, NativeAOT compiler, and Roslyn analyzer to understand
the pattern typeof(SomeType).Assembly.GetType("OtherType"). This allows
the analysis to resolve the target type and avoid false warnings.
- Add AssemblyValue (SingleValue holding assembly simple name)
- Add IntrinsicId.Type_get_Assembly and IntrinsicId.Assembly_GetType
- Handle Type_get_Assembly: SystemTypeValue -> AssemblyValue
- Handle Assembly_GetType: resolve type name within the known assembly
- Implement partials in all three consumers (ILLinker, NativeAOT, Roslyn)
- Add test coverage in AssemblyGetTypeDataFlow.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @agocke, @dotnet/illink |
There was a problem hiding this comment.
Pull request overview
This PR extends ILLink trim analysis (and its NativeAOT + Roslyn analyzer consumers) to understand the pattern typeof(T).Assembly.GetType(string) by modeling Type.Assembly and Assembly.GetType(...) as intrinsics, enabling more precise type resolution and reducing false trimming/dataflow warnings.
Changes:
- Introduces
AssemblyValueand new intrinsics (Type_get_Assembly,Assembly_GetType) to carry a known assembly identity through the dataflow lattice. - Implements
Type.Assembly→AssemblyValuepropagation andAssembly.GetType(...)resolution across ILLinker, NativeAOT, and the Roslyn analyzer. - Adds test coverage for the new analysis behavior in the ILLink test suite and wires it into analyzer test execution.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AssemblyGetTypeDataFlow.cs | New regression tests covering known/unknown Assembly.GetType patterns and ignoreCase behavior. |
| src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs | Adds a test entry point so the new dataflow case runs in analyzer test suite. |
| src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs | Linker-specific partial implementations for assembly-name extraction and in-assembly type resolution. |
| src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs | Recognizes Type.get_Assembly and Assembly.GetType(...) as intrinsics. |
| src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs | Adds intrinsic IDs for Type_get_Assembly and Assembly_GetType. |
| src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs | Shared intrinsic behavior: produces AssemblyValue from SystemTypeValue and resolves Assembly.GetType(...). |
| src/tools/illink/src/ILLink.Shared/TrimAnalysis/AssemblyValue.cs | New SingleValue representing a known assembly (by simple name). |
| src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TypeNameResolver.cs | Adds analyzer-side helper for resolving a metadata name within a specific assembly reference. |
| src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs | Analyzer partial implementations using the new resolver helper. |
| src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs | NativeAOT partial implementations for assembly-name extraction and in-assembly type resolution. |
… fallback - Add dedicated DiagnosticId.AssemblyGetTypeCannotBeAnalyzed (IL2128) for unknown assembly receivers instead of reusing IL2057 - Register IL2128 in DynamicallyAccessedMembersAnalyzer - Add resx strings for IL2128 title and message - Return annotatedMethodReturnValue (not Top) for non-SystemTypeValue in Type_get_Assembly handler - Remove unused using and unnecessary [Kept] from test - Update test to expect IL2128 for unknown assembly case Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the case where Type.Assembly is called on a non-SystemTypeValue (e.g. a method parameter) and then Assembly.GetType is called on the result. Verifies IL2128 is produced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the assembly receiver is unknown, the type name is unknown, or the case-insensitive flag is set, fall back to the original RequiresUnreferencedCode (IL2026) warning instead of producing new diagnostic IDs. This avoids a breaking change where code suppressing IL2026 for Assembly.GetType calls would get unsuppressed warnings. Add ReportRequiresUnreferencedCode partial method implemented in all three consumers (ILLinker, NativeAOT, Roslyn analyzer) to enable the shared handler to report the RUC warning. Remove unused IL2128 diagnostic that is no longer needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous Assembly.GetType data-flow analysis used a separate, less-capable
`TryResolveTypeNameInAssembly` path on the Roslyn analyzer side
(`IAssemblySymbol.GetTypeByMetadataName`) that didn't handle arrays,
pointers, or generic instantiations. The Cecil and AOT backends used the full
TypeName-parsing resolver but had a different gap: they fell back to the core
library for unqualified type names, which over-resolves for Assembly.GetType
(which only searches the receiver assembly at runtime).
Add a `fallbackToCoreLib` flag to all three resolvers and plumb it through
the marker layer. The Assembly.GetType call site now passes
`fallbackToCoreLib: false`; existing callers (Activator.CreateInstance,
custom attribute parser, UnsafeAccessor, LinkContext, test helpers) keep
`fallbackToCoreLib: true`. The Roslyn analyzer's TryResolveTypeNameInAssembly
now parses the input with TypeName.TryParse and dispatches to the existing
private `ResolveTypeName(assembly, typeName)` helper, which already handles
arrays, pointers, and constructed generics.
New tests in AssemblyGetTypeDataFlow exercise array, pointer, and generic
type names. TestTypeOnlyInCoreLib verifies that
`Assembly.GetType("System.Reflection.Assembly")` does not over-resolve to
corelib; if it did, RequiresAll() would mark Assembly.GetTypes() and other
RequiresUnreferencedCode-annotated members and emit IL2026.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tried the same on JIT side #127697. |
Restrict Type.Assembly modeling to named types. Function pointer types, array/pointer/byref wrappers, generic parameters, and System.Array itself are now rejected, returning null from GetAssemblyName so downstream Assembly.GetType calls fall back to an IL2026 warning. Function pointers in particular are a correctness concern: CoreCLR returns the loader module (effectively random) and NativeAOT returns CoreLib, so no static modeling is sound. System.Array is rejected to handle the Cecil linker's IL scanner, which lowers typeof(SomeType[]) to a TypeDefinition of System.Array. Without this rejection the linker would model typeof(SomeType[]).Assembly as CoreLib, which is wrong for non-CoreLib element types. Rejecting System.Array also covers the (rare) case where user code writes typeof(System.Array).Assembly directly. Type_get_Assembly no longer short-circuits on an empty receiver value: an unknown receiver has an unknown Assembly, and downstream Assembly.GetType should warn rather than silently widen to Top. The Roslyn analyzer's SingleValueExtensions.FromTypeSymbol is unchanged; function pointer typeof produces Top via the existing default arm, which now flows through to a warning. Tests cover array, function pointer, generic parameter, and array-of-T receivers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
At runtime, Assembly.GetType invokes the type name parser with
IsAssemblyGetType = true, which rejects any top-level assembly qualifier
(returns null, or throws Argument_AssemblyGetTypeCannotSpecifyAssembly
when throwOnError is true). The new dataflow analysis previously honored
the qualifier and resolved the type from the named assembly, causing
spurious marking of types and members the runtime would never see.
Reject top-level assembly-qualified names in each of the three resolvers
when called from the Assembly.GetType path:
- Cecil's Mono.Linker.TypeNameResolver: gated on !fallbackToCoreLib.
- AOT's CustomAttributeTypeNameParser: gated on !fallbackToCoreLib.
- Roslyn analyzer's TryResolveTypeNameInAssembly: unconditional (the
method is exclusive to Assembly.GetType).
Assembly qualifiers on generic arguments are still honored, matching
runtime behavior.
A regression test calls Assembly.GetType("System.Reflection.Assembly,
System.Runtime").RequiresAll(); without the fix, the analyzer over-resolves
to Assembly and RequiresAll marks RUC members like LoadFrom and emits
IL2026.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| { | ||
| // An unknown receiver has an unknown Assembly; downstream Assembly.GetType | ||
| // should warn rather than silently widen to Top. | ||
| returnValue = annotatedMethodReturnValue; |
There was a problem hiding this comment.
I think this should follow the pattern that other intrinsics use (check IsEmpty first, and if it's empty return Top and break). The IsEmpty case represents "no value", not "unknown value".
For consistency we could also add a case that returns Top on NullValue.Instance.
Mind adding a test for this too? There's an existing test pattern (look for TestNull/TestNoValue tests for the other intrinsics).
There was a problem hiding this comment.
Do you mean restoring the:
case IntrinsicId.Type_get_Assembly:
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}part that I removed in c1a65d9?
Analysis models function pointer types as Empty so there is the typeof(delegate*...).Assembly.GetType that would not work because we just keep propagating it as Top and no warning is triggered even though we don't know what type we just got.
There was a problem hiding this comment.
Yes - ah then I think that's a bug, and function pointer types should be modeled as "unknown" instead of "empty". I can fix that in a follow-up if you don't want to do so here.
Teach the trimmer, NativeAOT compiler, and Roslyn analyzer to understand the pattern typeof(SomeType).Assembly.GetType("OtherType"). This allows the analysis to resolve the target type and avoid false warnings.
This is an extremely common pattern in the libraries tests despite me fixing many instances up into
Type.GetType. It is somewhat nice to be able to do this though. So I've let copilot do it. We could also use this to implement dotnet/linker#1947 later.