Description
When the runtime-async feature is enabled in .NET 11 Preview 3 (11.0.100-preview.3.26207.106), compiling a NativeAOT application fails with an ILC crash if a generic method calls JsonSerializer.DeserializeAsync<T> passing a JsonTypeInfo<T> instance, where T is a generic type parameter. This pattern is commonly used for deserializing JSON into generic types (see minimal example below).
This is being hit in npgsql/npgsql#6488.
Related discussion: npgsql/npgsql#6488 (comment)
Minimal reproducing code
sealed class ReproConverter<T, TBase> where T : TBase?
{
readonly JsonTypeInfo _jsonTypeInfo;
public async ValueTask<T?> ReadAsync(bool async, Stream stream, CancellationToken cancellationToken)
{
return _jsonTypeInfo switch
{
// ❌ ILC crash with runtime-async enabled:
JsonTypeInfo<T> typeInfoOfT => async
? await JsonSerializer.DeserializeAsync(stream, typeInfoOfT, cancellationToken).ConfigureAwait(false)
: JsonSerializer.Deserialize(stream, typeInfoOfT),
_ => (T?)(async
? await JsonSerializer.DeserializeAsync(stream, (JsonTypeInfo<TBase?>)_jsonTypeInfo, cancellationToken).ConfigureAwait(false)
: JsonSerializer.Deserialize(stream, (JsonTypeInfo<TBase?>)_jsonTypeInfo))
};
}
}
See full minimal repro project for instructions and code: https://github.com/manandre/repro-dotnet-runtime-async-aot-json
Reproduction Steps
Run publish with runtime-async enabled (default):
dotnet publish -c Release # fails
Run publish with runtime-async disabled:
dotnet publish -c Release -p:RuntimeAsync=false # succeeds
Expected behavior
NativeAOT compilation should succeed when calling JsonSerializer.DeserializeAsync<T> in the described scenario.
Actual behavior
ILC crashes with the following error and stack trace:
EXEC : error : One or more errors occurred. (Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)') [test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj::TargetFramework=net11.0]
System.AggregateException: One or more errors occurred. (Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)')
---> ILCompiler.CodeGenerationFailedException: Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)'
---> System.InvalidOperationException: [Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>: MethodDictionary: [System.Text.Json]System.Text.Json.JsonSerializer.DeserializeAsync<__Canon>(Stream,JsonTypeInfo`1<__Canon>,CancellationToken)
at ILCompiler.DependencyAnalysis.PrecomputedDictionaryLayoutNode.TryGetSlotForEntry(GenericLookupResult, Int32&)
at ILCompiler.Compilation.ComputeGenericLookup(MethodDesc, ReadyToRunHelperId, Object)
at Internal.JitInterface.CorInfoImpl.ComputeLookup(CORINFO_RESOLVED_TOKEN&, Object, ReadyToRunHelperId, MethodDesc, CORINFO_LOOKUP&)
at Internal.JitInterface.CorInfoImpl._embedGenericHandle(IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, Byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*)
--- End of inner exception stack trace ---
at Internal.JitInterface.CorInfoImpl.CompileMethodInternal(IMethodNode, MethodIL)
at ILCompiler.RyuJitCompilation.CompileSingleMethod(CorInfoImpl, MethodCodeNode)
at Internal.JitInterface.CorInfoImpl.CompileSingleMethod(MethodCodeNode)
at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`2.<ForWorker>b__1(RangeWorker&, Int64, Boolean&)
--- End of stack trace from previous location ---
at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionOrOtherException(ICollection, CancellationToken, Exception)
at System.Threading.Tasks.Parallel.ForWorker[TLocal,TInt](TInt, TInt, ParallelOptions, Action`1, Action`2, Func`4, Func`1, Action`1)
at ILCompiler.RyuJitCompilation.CompileMultiThreaded(List`1)
at ILCompiler.RyuJitCompilation.ComputeDependencyNodeDependencies(List`1)
at ILCompiler.DependencyAnalysisFramework.DependencyAnalyzer`2.ComputeMarkedNodes()
at ILCompiler.RyuJitCompilation.CompileInternal(String, ObjectDumper)
at ILCompiler.Compilation.ILCompiler.ICompilation.Compile(String, ObjectDumper)
at ILCompiler.Program.Run()
at ILCompiler.ILCompilerRootCommand.<>c__DisplayClass264_0.<.ctor>b__0(ParseResult)
/home/runner/.nuget/packages/microsoft.dotnet.ilcompiler/11.0.0-preview.3.26207.106/build/Microsoft.NETCore.Native.targets(311,5): error MSB3073: The command ".../ilc ..." exited with code 1. [test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj::TargetFramework=net11.0]
Regression?
Unknown.
This specific crash appears in .NET 11 Preview 3 and is triggered by the new runtime-async implementation. This code pattern was not previously published using runtime-async + NativeAOT.
Known Workarounds
Cast the type info to the non-generic base JsonTypeInfo and use the non-generic overload:
JsonTypeInfo<T> typeInfoOfT => (T?)(async
? await JsonSerializer.DeserializeAsync(stream, (JsonTypeInfo)typeInfoOfT, cancellationToken).ConfigureAwait(false)
: JsonSerializer.Deserialize(stream, (JsonTypeInfo)typeInfoOfT),
This sacrifices type-safety and should not be required.
Configuration
.NET SDK: 11.0.100-preview.3.26207.106
OS: Linux x64 (GitHub Actions), reproduces locally and in CI
Repro: https://github.com/manandre/repro-dotnet-runtime-async-aot-json
Other information
First hit when integrating runtime-async feature in Npgsql (the .NET data provider for PostgreSQL). PR: npgsql/npgsql#6488
Area labels: area-System.Text.Json, area-NativeAOT-coreclr, runtime-async
Description
When the runtime-async feature is enabled in .NET 11 Preview 3 (11.0.100-preview.3.26207.106), compiling a NativeAOT application fails with an ILC crash if a generic method calls JsonSerializer.DeserializeAsync<T> passing a JsonTypeInfo<T> instance, where T is a generic type parameter. This pattern is commonly used for deserializing JSON into generic types (see minimal example below).
This is being hit in npgsql/npgsql#6488.
Related discussion: npgsql/npgsql#6488 (comment)
Minimal reproducing code
See full minimal repro project for instructions and code: https://github.com/manandre/repro-dotnet-runtime-async-aot-json
Reproduction Steps
cd ReproAsyncAotJsonRun publish with runtime-async enabled (default):
dotnet publish -c Release # failsRun publish with runtime-async disabled:
dotnet publish -c Release -p:RuntimeAsync=false # succeedsExpected behavior
NativeAOT compilation should succeed when calling JsonSerializer.DeserializeAsync<T> in the described scenario.
Actual behavior
ILC crashes with the following error and stack trace:
EXEC : error : One or more errors occurred. (Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)') [test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj::TargetFramework=net11.0] System.AggregateException: One or more errors occurred. (Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)') ---> ILCompiler.CodeGenerationFailedException: Code generation failed for method '[Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>.Read(bool,PgReader,CancellationToken)' ---> System.InvalidOperationException: [Npgsql]Npgsql.Internal.Converters.JsonConverter`2<System.__Canon,System.__Canon>: MethodDictionary: [System.Text.Json]System.Text.Json.JsonSerializer.DeserializeAsync<__Canon>(Stream,JsonTypeInfo`1<__Canon>,CancellationToken) at ILCompiler.DependencyAnalysis.PrecomputedDictionaryLayoutNode.TryGetSlotForEntry(GenericLookupResult, Int32&) at ILCompiler.Compilation.ComputeGenericLookup(MethodDesc, ReadyToRunHelperId, Object) at Internal.JitInterface.CorInfoImpl.ComputeLookup(CORINFO_RESOLVED_TOKEN&, Object, ReadyToRunHelperId, MethodDesc, CORINFO_LOOKUP&) at Internal.JitInterface.CorInfoImpl._embedGenericHandle(IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, Byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*) --- End of inner exception stack trace --- at Internal.JitInterface.CorInfoImpl.CompileMethodInternal(IMethodNode, MethodIL) at ILCompiler.RyuJitCompilation.CompileSingleMethod(CorInfoImpl, MethodCodeNode) at Internal.JitInterface.CorInfoImpl.CompileSingleMethod(MethodCodeNode) at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`2.<ForWorker>b__1(RangeWorker&, Int64, Boolean&) --- End of stack trace from previous location --- at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionOrOtherException(ICollection, CancellationToken, Exception) at System.Threading.Tasks.Parallel.ForWorker[TLocal,TInt](TInt, TInt, ParallelOptions, Action`1, Action`2, Func`4, Func`1, Action`1) at ILCompiler.RyuJitCompilation.CompileMultiThreaded(List`1) at ILCompiler.RyuJitCompilation.ComputeDependencyNodeDependencies(List`1) at ILCompiler.DependencyAnalysisFramework.DependencyAnalyzer`2.ComputeMarkedNodes() at ILCompiler.RyuJitCompilation.CompileInternal(String, ObjectDumper) at ILCompiler.Compilation.ILCompiler.ICompilation.Compile(String, ObjectDumper) at ILCompiler.Program.Run() at ILCompiler.ILCompilerRootCommand.<>c__DisplayClass264_0.<.ctor>b__0(ParseResult) /home/runner/.nuget/packages/microsoft.dotnet.ilcompiler/11.0.0-preview.3.26207.106/build/Microsoft.NETCore.Native.targets(311,5): error MSB3073: The command ".../ilc ..." exited with code 1. [test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj::TargetFramework=net11.0]Regression?
Unknown.
This specific crash appears in .NET 11 Preview 3 and is triggered by the new runtime-async implementation. This code pattern was not previously published using runtime-async + NativeAOT.
Known Workarounds
Cast the type info to the non-generic base JsonTypeInfo and use the non-generic overload:
This sacrifices type-safety and should not be required.
Configuration
.NET SDK: 11.0.100-preview.3.26207.106
OS: Linux x64 (GitHub Actions), reproduces locally and in CI
Repro: https://github.com/manandre/repro-dotnet-runtime-async-aot-json
Other information
First hit when integrating runtime-async feature in Npgsql (the .NET data provider for PostgreSQL). PR: npgsql/npgsql#6488
Area labels: area-System.Text.Json, area-NativeAOT-coreclr, runtime-async