Skip to content

Commit 1031b6e

Browse files
github-actions[bot]MichalStrehovskyjeffschwMSFT
authored
Fix handling of IDynamicInterfaceCastable wrt CastCache (#110007)
Fixes #108229. The actual fix is the addition of an `if` check where it originally wasn't. I also fixed the other checks for consistency - positive checks are fine to cache, and negative checks against non-interface targets are also fine to cache. Co-authored-by: Michal Strehovský <[email protected]> Co-authored-by: Jeff Schwartz <[email protected]>
1 parent 7bbc36d commit 1031b6e

File tree

2 files changed

+37
-16
lines changed

2 files changed

+37
-16
lines changed

src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,10 +1066,14 @@ private static unsafe bool CacheMiss(MethodTable* pSourceType, MethodTable* pTar
10661066
bool result = TypeCast.AreTypesAssignableInternalUncached(pSourceType, pTargetType, variation, &newList);
10671067

10681068
//
1069-
// Update the cache
1069+
// Update the cache. We only consider type-based conversion rules here.
1070+
// Therefore a negative result cannot rule out convertibility for IDynamicInterfaceCastable.
10701071
//
1071-
nuint sourceAndVariation = (nuint)pSourceType + (uint)variation;
1072-
s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, result);
1072+
if (result || !(pTargetType->IsInterface && pSourceType->IsIDynamicInterfaceCastable))
1073+
{
1074+
nuint sourceAndVariation = (nuint)pSourceType + (uint)variation;
1075+
s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, result);
1076+
}
10731077

10741078
return result;
10751079
}
@@ -1252,13 +1256,11 @@ internal static unsafe bool AreTypesAssignableInternalUncached(MethodTable* pSou
12521256
}
12531257

12541258
//
1255-
// Update the cache
1259+
// Update the cache. We only consider type-based conversion rules here.
1260+
// Therefore a negative result cannot rule out convertibility for IDynamicInterfaceCastable.
12561261
//
1257-
if (!pSourceType->IsIDynamicInterfaceCastable)
1262+
if (retObj != null || !(pTargetType->IsInterface && pSourceType->IsIDynamicInterfaceCastable))
12581263
{
1259-
//
1260-
// Update the cache
1261-
//
12621264
nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource;
12631265
s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, retObj != null);
12641266
}
@@ -1293,14 +1295,11 @@ private static unsafe object CheckCastAny_NoCacheLookup(MethodTable* pTargetType
12931295
obj = CheckCastClass(pTargetType, obj);
12941296
}
12951297

1296-
if (!pSourceType->IsIDynamicInterfaceCastable)
1297-
{
1298-
//
1299-
// Update the cache
1300-
//
1301-
nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource;
1302-
s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, true);
1303-
}
1298+
//
1299+
// Update the cache
1300+
//
1301+
nuint sourceAndVariation = (nuint)pSourceType + (uint)AssignmentVariation.BoxedSource;
1302+
s_castCache.TrySet(sourceAndVariation, (nuint)pTargetType, true);
13041303

13051304
return obj;
13061305
}

src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public static int Run()
4444
TestVariantInterfaceOptimizations.Run();
4545
TestSharedInterfaceMethods.Run();
4646
TestGenericAnalysis.Run();
47+
TestRuntime108229Regression.Run();
4748
TestCovariantReturns.Run();
4849
TestDynamicInterfaceCastable.Run();
4950
TestStaticInterfaceMethodsAnalysis.Run();
@@ -738,6 +739,27 @@ public static void Run()
738739
}
739740
}
740741

742+
class TestRuntime108229Regression
743+
{
744+
class Shapeshifter : IDynamicInterfaceCastable
745+
{
746+
public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) => throw new NotImplementedException();
747+
public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) => true;
748+
}
749+
750+
[MethodImpl(MethodImplOptions.NoInlining)]
751+
static bool Is(object o) => o is IEnumerable<object>;
752+
753+
public static void Run()
754+
{
755+
object o = new Shapeshifter();
756+
757+
// Call multiple times in case we just flushed the cast cache (when we flush we don't store).
758+
if (!Is(o) || !Is(o) || !Is(o))
759+
throw new Exception();
760+
}
761+
}
762+
741763
class TestCovariantReturns
742764
{
743765
interface IFoo

0 commit comments

Comments
 (0)