-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow MakeGenericType
/MakeArrayType
of arbitrary types
#112986
Conversation
When we worked on .NET Native, we didn't have static analysis but at least wanted to have some predictability in how dynamic code fails with AOT. We decided we want `MakeGenericType` & co. to be the thing that fails instead of subsequent operations that try to obtain a type handle (one needs a type handle to do "interesting" reflection operations). As we refined the model and introduced static analysis, we decided it's fine if trim/AOT unsafe code can get at things that are in "undefined" state (named types that might not have type handles, MethodInfos that don't have parameter names, etc.). The static analysis warning is always at the problematic operation and what happens next can be anything. But `MakeGenericType` failing early sometimes got in the way. For example xUnit uses `MakeGenericType` to create a new interface at runtime and check `IsAssignableFrom` with it. We don't actually need a usable type handle for this. We had an opt-out switch that we only enabled for testing to get xUnit working. But that also means we were not testing the shipping configuration whenever xUnit was involved. This PR makes it so `MakeGeneric`/`MakeArray` can actually succeed even if we don't have the code/data structures for the type. Trying to do "interesting" reflection with it is going to throw the good old MissingMetadata-like exception - the difference is just in the timing. I'm also changing things to allow obtaining `TypeHandle` on generic types even if they're in the "necessary" form only. This will only bring us to parity with what already happens for non-generic types. We already have blocks in places to make sure such `TypeHandle` cannot be used for much more than casting (e.g. `GetUninitializedObject` has a preexisting block for this). We say we're fine with undefined behavior for trim-unsafe code, but that does not include GC holes.
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
/azp run runtime-nativeaot-outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
Outdated
Show resolved
Hide resolved
I think the part about allowing this API to succeed always can be up to debate. For #111610 I'd only need the part where we can obtain a I just thought that if we're allowing a limited I'm still a bit squeamish about these |
I think it is fine to be as lazy as possible when throwing missing metadata exceptions. Users who follow the guidance and pay attention to the static analysis should never see these exceptions. Should the same change be made in MakeGenericMethod for parity? Line 147 in 6d344b3
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
Sounds reasonable. Turns out grabbing the method invoker early was also validating the MakeGenericMethod doesn't break constraints. So added a check for that. (We do this check implicitly for MakeGenericType because we attempt to get a TypeHandle as part of System.Type interning logic.) |
When we worked on .NET Native, we didn't have static analysis but at least wanted to have some predictability in how dynamic code fails with AOT. We decided we want
MakeGenericType
& co. to be the thing that fails instead of subsequent operations that try to obtain a type handle (one needs a type handle to do "interesting" reflection operations).As we refined the model and introduced static analysis, we decided it's fine if trim/AOT unsafe code can get at things that are in "undefined" state 1 (named types that might not have type handles, MethodInfos that don't have parameter names, etc.). The static analysis warning is always at the problematic operation and what happens next can be anything.
But
MakeGenericType
failing early sometimes got in the way. For example xUnit usesMakeGenericType
to create a new interface at runtime and checkIsAssignableFrom
with it. We don't actually need a usable type handle for this. We had an opt-out switch that we only enabled for testing to get xUnit working. But that also means we were not testing the shipping configuration whenever xUnit was involved.This PR makes it so
MakeGeneric
/MakeArray
can actually succeed even if we don't have the code/data structures for the type. Trying to do "interesting" reflection with it is going to throw the good old MissingMetadata-like exception - the difference is just in the timing.I'm also changing things to allow obtaining
TypeHandle
on generic types even if they're in the "necessary" form only. This will only bring us to parity with what already happens for non-generic types. We already have blocks in places to make sure suchTypeHandle
cannot be used for much more than casting (e.g.GetUninitializedObject
has a preexisting block for this).Cc @dotnet/ilc-contrib
Footnotes
We say we're fine with undefined behavior for trim-unsafe code, but that does not include GC holes. ↩