Skip to content
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

Error in ImmutableArray initialization using C#12 collection literals #92022

Closed
eduherminio opened this issue Sep 13, 2023 · 19 comments
Closed
Labels
area-Tools-ILLink .NET linker development as well as trimming analyzers
Milestone

Comments

@eduherminio
Copy link
Member

Description

ImmutableArrays 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:

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

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Sep 13, 2023
@stephentoub stephentoub removed the untriaged New issue has not been triaged by the area owner label Sep 13, 2023
@stephentoub stephentoub transferred this issue from dotnet/runtime Sep 13, 2023
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Sep 13, 2023
@agocke agocke transferred this issue from dotnet/roslyn Sep 13, 2023
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Sep 13, 2023
@agocke
Copy link
Member

agocke commented Sep 13, 2023

Looks like a bug in the linker.

The compiler-synthesized InlineArray type has the following IL in the untrimmed code:

.class private auto ansi sealed beforefieldinit '<>y__InlineArray64'<T>
	extends [System.Runtime]System.ValueType
{
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.InlineArrayAttribute::.ctor(int32) = (
		01 00 40 00 00 00 00 00
	)
	// Fields
	.field private !T _element0
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
		01 00 00 00
	)

} // end of class <>y__InlineArray64

while it has this IL in the trimmed code:

.class private auto ansi sealed beforefieldinit '<>y__InlineArray64'<T>
	extends [System.Private.CoreLib]System.ValueType
{
	.custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InlineArrayAttribute::.ctor(int32) = (
		01 00 40 00 00 00 00 00
	)
} // end of class <>y__InlineArray64

@agocke
Copy link
Member

agocke commented Sep 13, 2023

Note the following from the draft C# language spec, which presumably indicates a runtime requirement:

Since there is a guarantee that the first element in an inline array type is aligned at the beginning of the type (no gap), compiler will use the following code to get a Span value:

That implies that types with InlineArray need to have their fields preserved.

@agocke
Copy link
Member

agocke commented Sep 13, 2023

@sbomer @vitek-karas

@agocke agocke added this to the 8.0.0 milestone Sep 13, 2023
@agocke agocke added this to AppModel Sep 13, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Sep 13, 2023
@stephentoub stephentoub added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Sep 13, 2023
@ghost
Copy link

ghost commented Sep 13, 2023

Tagging subscribers to this area: @agocke, @sbomer, @vitek-karas
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

ImmutableArrays 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:

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

Author: eduherminio
Assignees: -
Labels:

area-Tools-ILLink, needs-area-label

Milestone: 8.0.0

@stephentoub stephentoub removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Sep 13, 2023
@eduherminio
Copy link
Member Author

eduherminio commented Sep 13, 2023

Looks like a bug in the linker.

@agocke Would that also explain the error when debugging from VS? For me that's the most puzzling part

@lsoft
Copy link

lsoft commented Sep 14, 2023

@agocke

Probably, I see something related when debugging immutables inside VS:

image

image

image

image

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.

@vitek-karas
Copy link
Member

#92060 should fix the problem with trimmed app using these.

@vitek-karas
Copy link
Member

I don't think the VS problem is in any way related to the trimming problem.

@eduherminio Could you please describe what exactly you did in VS to see the NullReferenceException? I tried to debug the app and it seems to work mostly fine. The only rough edge I see is when inspecting the array in a Watch window:
image

And expanding the results fails with a rather cryptic error.

@AaronRobinsonMSFT was there a followup to the diagnostics work mentioned in #81145?

@vitek-karas
Copy link
Member

I can repro a similar problem as @lsoft ... I'll follow up on the debugger part.

@eduherminio
Copy link
Member Author

eduherminio commented Sep 14, 2023

@eduherminio Could you please describe what exactly you did in VS to see the NullReferenceException?

Simplest repro: clone https://github.com/eduherminio/dotnet-runtime-issue-92022, open it in VS and F5, but I think you're already into it.

immutable_

@AaronRobinsonMSFT
Copy link
Member

@AaronRobinsonMSFT was there a followup to the diagnostics work mentioned in #81145?

Not to my knowledge. I can take a look later today if needed though. The question is going to be if this is on the VS or runtime side, which boils down to - are we giving VS all the details it needs or does it need more?

/cc @VSadov

@vitek-karas
Copy link
Member

Thanks @eduherminio - for me a simple F5 works... but it does look very similar to the other failure from watch. What version of VS/.NET are you using?

@vitek-karas
Copy link
Member

vitek-karas commented Sep 14, 2023

@AaronRobinsonMSFT - I tracked the answer internally - thanks a lot.

Right now, it seems it's mostly a lack of functionality on the VS side - and it's being worked on.

@eduherminio
Copy link
Member Author

@vitek-karas let me know if any other extra information is needed:

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

@vitek-karas
Copy link
Member

@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:

Console.WriteLine("Break here"); // Stopping here in the debugger Constants.V shows 0

Console.WriteLine(Constants.V); // This prints out 42

class Constants
{
    public static readonly int V = 42;
}

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.

@eduherminio
Copy link
Member Author

eduherminio commented Sep 14, 2023

@vitek-karas Let me know if I can help in any other way about it.
For now I checked the following:

  • It crashes running it in Release mode from
  • It crashes building in both Debug and Release mode from VS and then running them from cli

I can perhaps provide the .exe files built from VS, which also crash for me locally when ran from console, if that helps.

@vitek-karas
Copy link
Member

The problem when trimming is now fixed in both main and net8.0 branches, so it should ship with 8 RC2 I think.

@agocke
Copy link
Member

agocke commented Oct 2, 2023

Closing as fixed

@agocke agocke closed this as completed Oct 2, 2023
@eduherminio
Copy link
Member Author

Fix verified in RC2.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Tools-ILLink .NET linker development as well as trimming analyzers
Projects
Archived in project
Development

No branches or pull requests

6 participants