JIT: (bug) `Enum.Equals` folding throws `NullReferenceException` instead of returning `false` for a `null` argument
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
`Compiler::gtFoldExprCall` folds `NI_System_Enum_Equals` into a direct comparison of the two boxed payloads without checking that the *argument* is non-null, so a `null` argument is unconditionally dereferenced. `ValueType.Equals(object)` must return `false` for `null`, never throw.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
enum E : int { A = 0, B = 1 }
class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static bool Test(E x, E? y)
{
object a = x;
object b = y; // null when y is null, but the JIT still sees exact class E
return a.Equals(b);
}
static void Main() => Console.WriteLine(Test(E.A, null));
}
```
### Expected
```
False
```
### Actual
```
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at Program.Test(E x, Nullable`1 y)
```
The generated code has the `null` path (`xor rax, rax`) flowing straight into `cmp ecx, dword ptr [rax+0x08]`.
### Notes
The fold requires only an exact class handle for both operands and explicitly discards the `isNonNull` result (`bool isNonNull; // Unused here.`). Boxing a `Nullable` yields either a boxed `TEnum` (exact handle) or `null`, so exactness does not imply non-null. FullOpts only; MinOpts/tier-0 are correct. Does not repro on .NET 10 — regression from d3f4519befd ("Intrinsify Enum.Equals to avoid boxing", #122779).
Contributor guide
Assessment
This issue has not been assessed yet.