JIT: (bug) assert `unreached` in `UpdateAncestorTypes` for `GT_STORE_LCL_FLD` with `(S?)(object)s` and a stack-allocated field
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
`ObjectAllocator::UpdateAncestorTypes` has no case for `GT_STORE_LCL_FLD` and falls through to the `default:` label, which does `unreached()` (objectalloc.cpp:2697). `(S?)(object)s` is folded by `impStoreNullableFields` into a `GT_STORE_LCL_FLD` whose data operand is the tracked struct local.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
public class Program
{
struct S { public object O; }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static bool Test()
{
S s = default;
s.O = new object();
S? ns = (S?)(object)s;
return ns.HasValue;
}
static void Main() => Console.WriteLine(Test());
}
```
Run with `DOTNET_TieredCompilation=0`.
### Expected
No assertion; the program prints `True`.
### Actual
```
Assert failure(PID 42452 [0x0000a5d4], Thread: 8312 [0x2078]): Assertion failed 'unreached' in
'Program:Test():bool' during 'Allocate Objects' (IL size 40; hash 0xc3f885e8; FullOpts)
File: src\coreclr\jit\objectalloc.cpp:2697
```
Exit code `0xC0000409`. JitDump shows the unhandled node:
```
UpdateAncestorTypes: unexpected op STORE_LCL_FLD in [000014]
```
### Notes
- With `DOTNET_JitObjectStackAllocation=0` the program prints `True` and exits 0.
- Also reproduces with a second `int` field in `S` and `return ns.Value.I;`.
- `AnalyzeParentStack` models this shape and keeps the local as a stack-allocation candidate, but the rewrite step then hits the unhandled opcode.
- Checked-JIT-only assertion; .NET 10 check skipped.
Contributor guide
Assessment
This issue has not been assessed yet.