-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Fix NativeAOT with minimal actions #35167
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
Conversation
- Generic methods for value types need to be visible at compile time so that the AOT compiler can generate code for the right instantiations (closed generic types). Since Enum.TryParse<T> is used purely via reflection is gets removed from the final AOT binary. This causes our logic that looks for Enum.TryParse<T> to fail. Instead we call back to the non-generic overload which doesn't get trimmed and can at runtime lookup the metadata for the enum type in order to allow parsing. - Made TryParseMethodCache creatable so it could be tested more easily. - Added tests
Co-authored-by: Martin Costello <martin@martincostello.com>
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
{ | ||
var staticEnumMethods = typeof(Enum).GetMethods(BindingFlags.Public | BindingFlags.Static); | ||
|
||
// With NativeAOT, if there's no static usage of Enum.TryParse<T>, it will be removed | ||
// we fallback to the non-generic version if that is the case |
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.
Is there a reason the non-generic one does not get trimmed?
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.
I assume it's because something else uses it. I have no idea what.
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.
Should we force it to be kept (e.g using DynamicDependency
)
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.
I'm not sure if it's needed TBH, I think typeof(Enum).GetMethods(BindingFlags.Public | BindingFlags.Static)
is visible to the linker but I'd need to make sure.
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.
This is surprising to me.
var staticEnumMethods = typeof(Enum).GetMethods(BindingFlags.Public | BindingFlags.Static);
That should "mark" all public static methods on Enum
, and the AOT compiler shouldn't trim any of them.
Is it possible to build up the GetMethod
query correctly so you only select the one method you are looking for?
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.
typeof(Enum).GetMethods(BindingFlags.Public | BindingFlags.Static) is visible to the linker but I'd need to make sure.
Yes. It is a bug in NativeAOT: dotnet/runtimelab#1402
Once this bug gets fixed, this will be broken again.
FWIW, I think fallbacks like this are wrong way to fix trimming and AOT friendliness issues.
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.
I didn't realize this is going to return TryParse<T>
if that method got reflection-rooted through other means. The fact that it's not just rooted from GetMethod
is kind of a bug but it only happens because the compiler figured there's not much it can do there - by default the AOT compiler will try to enable metadata for the method and generate a shared instantiation (over reference types), but the constraints on TryParse<T>
rule out reference types. Right now, because it can't make a usable method body, it will not generate the metadata either. But we can fix it to create the metadata.
Yes, this is likely going to fail at MakeGenericMethod
later.
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.
Once this bug gets fixed, this will be broken again.
Thanks. I'll look into that.
FWIW, I think fallbacks like this are wrong way to fix trimming and AOT friendliness issues.
It just needs to work well enough (or barely work) to enable people to try it out and give feedback. Seems like we need something a bit more creative here (like a check for IsDynamicCodeCompiled maybe). Remember it's using the expression tree interpreter here so the bar is low 😄
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.
IsDynamicCodeSupported is what I did in the DataContractSerializer as a temporary workaround while we figure things out: https://github.com/dotnet/runtimelab/pull/692/files#diff-5c36d3b2cc664a8e51d53ac77390c98eca051ca63b13443233afc38ccee5e65fR60-R64
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.
Yep, this is what I'll do. FWIW I have a pretty good handle on most of the AOT friendly problems that will plague ASP.NET Core (like JSON 😄 ) and have prototypes for how to tackle most of them.
Co-authored-by: Pranav K <prkrishn@hotmail.com>
Fixes #34875
PS: To get a better sense of what the exception looks like for not having the code for a specific generic instantiation:
Also, to see how this all of this expression compilation works with native AOT, the expression tree interpreter is used: