diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs index 5ea530800784d1..5eaedf3977acf0 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -1228,10 +1228,9 @@ internal static unsafe bool AreTypesAssignableInternalUncached(MethodTable* pSou } // - // Update the cache. We only consider type-based conversion rules here. - // Therefore a negative result cannot rule out convertibility for IDynamicInterfaceCastable. + // Update the cache // - if (retObj != null || !(pTargetType->IsInterface && pSourceType->IsIDynamicInterfaceCastable)) + if (!pSourceType->IsIDynamicInterfaceCastable || !pTargetType->IsInterface) { nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource; s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, retObj != null); @@ -1270,8 +1269,11 @@ private static unsafe object CheckCastAny_NoCacheLookup(MethodTable* pTargetType // // Update the cache // - nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource; - s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, true); + if (!pSourceType->IsIDynamicInterfaceCastable || !pTargetType->IsInterface) + { + nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource; + s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, true); + } return obj; } diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index e42a781d37df4c..3ad346ec4e880e 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -64,6 +64,7 @@ public static int Run() TestDynamicStaticGenericVirtualMethods.Run(); TestRuntime109496Regression.Run(); TestRuntime113664Regression.Run(); + TestRuntime125577Regression.Run(); return Pass; } @@ -2046,4 +2047,30 @@ static string Frob() where T : class, IFoo where U : class where V : return T.Frob(); } } + + class TestRuntime125577Regression + { + class Shapeshifter : IDynamicInterfaceCastable + { + bool _result; + public Shapeshifter(bool result) => _result = result; + + public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) => throw new NotImplementedException(); + public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) => _result; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Is(object o, bool expected) => o is IEnumerable == expected; + + public static void Run() + { + // Call multiple times in case we just flushed the cast cache (when we flush we don't store). + if (!Is(new Shapeshifter(true), true) + || !Is(new Shapeshifter(false), false) + || !Is(new Shapeshifter(true), true) + || !Is(new Shapeshifter(false), false)) + throw new Exception(); + } + } + }