Open hez2010 opened 4 years ago
EntityFrameworkCore has infinite generic expansion here in ExecuteImplementation and ExecuteImplementationAsync methods. These two methods call themselves with different generic arguments. It makes them unfriendly to AOT compilation, and incompatible with full AOT compilation.
https://github.com/dotnet/efcore/blob/master/src/EFCore/Storage/ExecutionStrategy.cs#L183
@jkotas AFAIK EF Core did work with UWP though?
.NET Native for UWP used universal shared generics to handle these cases. The code worked, but run number of times slower. Universal shared generics are not really full AOT compilation.
Note that the infinite generic expansion does not work too well with CoreCLR partial AOT either: crossgen will try to compile several expansions (that is usually a lot more than what is actually required) and eventually gives up.
Ah, interesting trick, is there an easy of finding the source of these generic expansion issues? I've tried it with DependencyViewer last time around but got lost.
I diagnose these by looking at the stacktrace of the stackoverflow, and then correlate it with sources. I agree that it would be nice to have better diagnostics for this in the compiler - https://github.com/dotnet/corert/issues/363
Marking this as up-for-grabs. This should be fixed by submitting PR to EFCore that removes the recursive call in ExecuteImplementation/ExecuteImplementationAsync.
@jkotas Per discussion a few weeks back with Hunter, any AOT solution needs to handle real .NET code including EF Core, not just a limited subset of simpler .NET code.
@ajcvickers We have an ongoing discussion on this topic and the answer is more nuanced than this.
With this kind of generic thing or generic reflection thing. Auto detect at compile time what generics run into this infinite expansion PLUS for dynamically created generics using runtime reflection just don't AOT them directly. You could just dynamically generate the memory at runtime similar to the .NET JIT for whats needed in the generic. Stuff will no longer inline maybe but it might solve the issue.
Would this kind of thing solve the issue?
dynamically generate the memory at runtime
How is it different from just having JIT?
Its not really but unless I'm missing something, .NET having generics done the way it did (by putting them in the IL instead of letting langs compile them out to standard objects like Java) either means you have dynamic code generation for them or you add a Syntax Analyzer for CoreRT and disable / give compiler errors for problematic generics and give runtime errors for generic object creation using reflection.
Or I just saw that I missed the post about "universal shared generics" which I'm guessing is having the generic objects compile out using dynamic types for generic parameters of some kind?
Maybe its just me but designing APIs around reflection to generate generic objects isn't something other langs / runtimes can even do and I don't see people complaining about it. Having the minority of people update their code based to run on what could be considered a .NET runtime subset is a good enough solution for 99% of code out there?
Someone had linked this on Twitter so just got me thinking is all.
CoreRT Stack overflow when build with EntityFrameworkCore
Repro:
Create project:
And then add an empty DbContext:
Build (with ilcarg: --verbose --singlethreaded):
Result: