runtime: Error in `ImmutableArray` initialization using C#12 collection literals
Description
ImmutableArray
s are throwing an exception when being initialized at runtime in some circumstances:
- When debugging the project from Visual Studio
- When publishing the project (self-contained, trimmed, single-file in my case).
However, they work from CLI
This can be especially painful because tests pass CI but then everything collapses at runtime initialization. Real life CI example:
- This CI passes: https://github.com/lynx-chess/Lynx/actions/runs/6165990734
- The windows executable can be downloaded from there fails.
- Checking out the branch (
immutable-arrays-cleaned
) and running it locally has the same behavior: works OK from command line, fails from VS
Simplified example below:
Reproduction Steps
dotnet new console -n immutable
Program.cs
using System.Collections.Immutable;
Console.WriteLine($"Hello, World from {Constants.Coordinates[0]}");
public static class Constants
{
public static readonly ImmutableArray<string> Coordinates =
[
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"
];
}
immutable.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Expected behavior
Runs normally, prints this in all the cases
Hello, World from a8
Actual behavior
Failes from Visual Studio or when the project is published:
$> dotnet run -c Release
Hello, World from a8
$> dotnet run -c Debug
Hello, World from a8
$> dotnet publish -c Release --self-contained /p:PublishSingleFile=true /p:PublishTrimmed=true --runtime win-x64 -o ./output && output/immutable.exe
Unhandled exception. System.TypeInitializationException: The type initializer for 'Constants' threw an exception.
---> System.TypeLoadException
at Constants..cctor()
--- End of inner exception stack trace ---
at Program.<Main>$(String[]) in C:\dev\tmp\immutable\Program.cs:line 3
C:\dev\tmp\immutable>
From Visual Studio, while debugging:
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=System.Collections.Immutable
StackTrace:
at System.Collections.Immutable.ImmutableArray`1.Add(T item)
at Constants..cctor() in C:\dev\tmp\immutable\Program.cs:line 8
Regression?
No response
Known Workarounds
No response
Configuration
.NET SDK:
Version: 8.0.100-rc.1.23455.8
Commit: e14caf947f
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19045
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\8.0.100-rc.1.23455.8\
.NET workloads installed:
There are no installed workloads to display.
Host:
Version: 8.0.0-rc.1.23419.4
Architecture: x64
Commit: 92959931a3
RID: win-x64
Microsoft Visual Studio Enterprise 2022 (64-bit) - Version 17.7.0
Other information
No response
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 19 (18 by maintainers)
The problem when trimming is now fixed in both
main
and net8.0 branches, so it should ship with 8 RC2 I think.I can repro a similar problem as @lsoft … I’ll follow up on the debugger part.
@eduherminio I can’t repro the app’s crash with F5.
The debugger differences are potentially explainable though. It seems the debugger doesn’t run static constructors just to show the right values for static fields.
For example:
Runtime will run the static constructor (which is what actually assigns the value 42 into the field) before any access to that field, but it does so as late as possible - so in this case it does it right before it reads the value of the field to pass it to WriteLine. The debugger doesn’t seem to trigger it on its own, so it sees the “uninitialized” values before that happens. In the case of ImmutableArray it’s possible that there’s something inside the array which is a reference and when it’s not initialized it’s null -> causing NullRef trying to use it from the debugger. But that should never happen at runtime.
https://github.com/dotnet/runtime/pull/92060 should fix the problem with trimmed app using these.
@agocke
Probably, I see something related when debugging immutables inside VS:
Unfortunately, I can’t repro this inside a small project, and can’t share the big project. The things above is everything I can share. Hope this helps.