Skip to content

Generic type check allocates for nullable structs #95685

@kindermannhubert

Description

@kindermannhubert

Description

The following two methods (M1, M2) should behave identically:

public static bool M1<T>(T value) => value is S;

public static bool M2<T>(T value)
{
    if (typeof(T) == typeof(S))
    {
        return true;
    }
    else if (typeof(T) == typeof(S?))
    {
        return Unsafe.As<T, S?>(ref value).HasValue;
    }
    return false;
}

However, to my surprise, M1 seems to allocate when T is a nullable struct.

Here's SharpLab's link: https://sharplab.io/#v2:EYLgtghglgdgNAFxBAzmAPgAQEwEYCwAUJgAwAEAygBYQBOADgDITAB0ASgK4wJRgCmAbiKkymXB268BrAMIB7MPSgAbfrQrqAblADG/FMMJEUCWp10JKZAN4BfEdjKyyRG0TKeyAbQBSUBABxfhh1PQAKBABPen55ADNwigBKZIBdDy8/AODQ2gjo2ITw2ARUjMIvH38gkLDdSJi4xIoAfnLMz2zavIKm4tL29M6xAGYxXAA2MmB5eRUyAFlcAB4AFQA+cLWyLQgVTn5ksgBeDd39w7IoFEojEe7c+sailo7KrJqn/IbC5pKeO8qo86j8Xv82kDPjlQX1XgCEEMKlVMONxNNZvMlth1lsdnsDkcRu4PlVPFB4mRwcU1scTicyH9iilkiMqiSyZyxAB2RnmIRsrwOUlk/gqFD8a6U6mJWmnBlMlpDQWeDlclG8gCqMBQEHi/FYAEEUOs4JRWltaPxKQTDslWAAJVAANUuApFVWF6swvPi+wlRk9RDsQA

When T is a non-nullable struct, the JIT is smart enough to know the result beforehand.
When T is a nullable struct the resulting code calls System.Runtime.CompilerServices.RuntimeHelpers.Box(System.Runtime.CompilerServices.MethodTable*, Byte ByRef).

To confirm, I profiled the following code:

long x = 0;
S? value = new S();
for (long i = 0; i < long.MaxValue - 1; i++)
{
    x += M1(value) ? 0 : 1;
}
Console.WriteLine(x);

I used Visual Studio with .NET Object Allocation Tracking, and it confirmed that M1 allocates while M2 does not.

These observations also hold for a slightly different version (from which I came) but I suppose it has the same underlying issue:

interface I { }
struct S : I { }

static bool M1<T>(T value) => value is I;

I believe this simple code definitely should not allocate.

Configuration

Windows 10, .NET 7 and .NET 8.

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issue

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions