NativeAOT: `IDynamicInterfaceCastable` ignored — `is` returns false and the cast crashes
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Repro
```csharp
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// IMarker's only implementor is sealed.
interface IMarker { }
sealed class Ordinary : IMarker { }
// IMarker2's implementor has a subclass (control case).
interface IMarker2 { }
class OpenBase : IMarker2 { }
sealed class OpenDerived : OpenBase { }
sealed class Dynamic : IDynamicInterfaceCastable
{
public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented)
=> interfaceType.Equals(typeof(IMarker).TypeHandle)
|| interfaceType.Equals(typeof(IMarker2).TypeHandle);
public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType)
=> throw new NotSupportedException();
}
static class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static bool Is(object value) => value is IMarker;
[MethodImpl(MethodImplOptions.NoInlining)]
static bool IsControl(object value) => value is IMarker2;
[MethodImpl(MethodImplOptions.NoInlining)]
static string Cast(object value)
{
try { return ((IMarker)value) is null ? "null" : "ok"; }
catch (InvalidCastException) { return "InvalidCastException"; }
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
static bool Baseline(object value) => value is IMarker;
static int Main()
{
object value = new Dynamic();
Console.WriteLine($"sanity (Ordinary is IMarker): {Is(new Ordinary())}");
Console.WriteLine($"baseline (no optimization) : {Baseline(value)}");
Console.WriteLine($"control (non-exact impl) : {IsControl(value)}");
Console.WriteLine($"optimized (is IMarker) : {Is(value)}");
Console.WriteLine($"optimized (cast to IMarker) : {Cast(value)}");
return 0;
}
}
```
Add to the project file:
```xml
true
```
## Expected
```
sanity (Ordinary is IMarker): True
baseline (no optimization) : True
control (non-exact impl) : True
optimized (is IMarker) : True
optimized (cast to IMarker) : ok
```
This is what CoreCLR prints for the same IL.
## Actual
```
sanity (Ordinary is IMarker): True
baseline (no optimization) : True
control (non-exact impl) : True
optimized (is IMarker) : False
```
The last line never prints; the process exits with `-2147483645` (`0x80000003`) inside `Cast`.
## Platform
Windows x64, NativeAOT with the stock .NET 11.0.100-rc.1.26425.128 SDK. CoreCLR is unaffected.
Contributor guide
Research direction
Start by running the supplied C# repro with DynamicInterfaceCastableSupport enabled and compare the NativeAOT output with CoreCLR. Check the optimized `is` and cast paths represented by `Is` and `Cast`; done means NativeAOT reports `True` and `ok` without terminating inside the cast.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100