Skip to content

Commit 631389f

Browse files
Fix ordering in default interface method lookup (#67379)
* Fix ordering in default interface method lookup The previous implementation would find a default implementation in the current type before looking for non-default implementation in the base. Default implementations should be looked at last. In fact the default implementation in the current type is unreachable if there's a non-default implementation in the base type - the compiler shouldn't even have bothered to emit it into the dispatch map. That can be a separate fix - this fix is still logically correct and necessary to get variant dispatch corner cases correctly. * Regression test
1 parent 43ab6b8 commit 631389f

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,16 @@ public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtTy
3030
if (pItfType->IsCloned)
3131
pItfType = pItfType->CanonicalEEType;
3232

33+
// We first look at non-default implementation. Default implementations are only considered
34+
// if the "old algorithm" didn't come up with an answer.
35+
bool fDoDefaultImplementationLookup = false;
36+
37+
again:
3338
while (pCur != null)
3439
{
3540
ushort implSlotNumber;
3641
if (FindImplSlotForCurrentType(
37-
pCur, pItfType, itfSlotNumber, &implSlotNumber))
42+
pCur, pItfType, itfSlotNumber, fDoDefaultImplementationLookup, &implSlotNumber))
3843
{
3944
IntPtr targetMethod;
4045
if (implSlotNumber < pCur->NumVtableSlots)
@@ -63,13 +68,23 @@ public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtTy
6368
else
6469
pCur = pCur->NonArrayBaseType;
6570
}
71+
72+
// If we haven't found an implementation, do a second pass looking for a default implementation.
73+
if (!fDoDefaultImplementationLookup)
74+
{
75+
fDoDefaultImplementationLookup = true;
76+
pCur = pTgtType;
77+
goto again;
78+
}
79+
6680
return IntPtr.Zero;
6781
}
6882

6983

7084
private static bool FindImplSlotForCurrentType(MethodTable* pTgtType,
7185
MethodTable* pItfType,
7286
ushort itfSlotNumber,
87+
bool fDoDefaultImplementationLookup,
7388
ushort* pImplSlotNumber)
7489
{
7590
bool fRes = false;
@@ -87,17 +102,11 @@ private static bool FindImplSlotForCurrentType(MethodTable* pTgtType,
87102

88103
if (pTgtType->HasDispatchMap)
89104
{
90-
// We first look at non-default implementation. Default implementations are only considered
91-
// if the "old algorithm" didn't come up with an answer.
92-
93-
bool fDoDefaultImplementationLookup = false;
94-
95105
// For variant interface dispatch, the algorithm is to walk the parent hierarchy, and at each level
96106
// attempt to dispatch exactly first, and then if that fails attempt to dispatch variantly. This can
97107
// result in interesting behavior such as a derived type only overriding one particular instantiation
98108
// and funneling all the dispatches to it, but its the algorithm.
99109

100-
again:
101110
bool fDoVariantLookup = false; // do not check variance for first scan of dispatch map
102111

103112
fRes = FindImplSlotInSimpleMap(
@@ -109,13 +118,6 @@ private static bool FindImplSlotForCurrentType(MethodTable* pTgtType,
109118
fRes = FindImplSlotInSimpleMap(
110119
pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup, fDoDefaultImplementationLookup);
111120
}
112-
113-
// If we haven't found anything and haven't looked at the default implementations yet, look now
114-
if (!fRes && !fDoDefaultImplementationLookup)
115-
{
116-
fDoDefaultImplementationLookup = true;
117-
goto again;
118-
}
119121
}
120122

121123
return fRes;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,13 @@ interface IFoo<T>
495495

496496
class Foo<T> : IFoo<T> { }
497497

498+
class Base : IFoo
499+
{
500+
int IFoo.GetNumber() => 100;
501+
}
502+
503+
class Derived : Base, IBar { }
504+
498505
public static void Run()
499506
{
500507
Console.WriteLine("Testing default interface methods...");
@@ -508,6 +515,9 @@ public static void Run()
508515
if (((IFoo)new Baz()).GetNumber() != 100)
509516
throw new Exception();
510517

518+
if (((IFoo)new Derived()).GetNumber() != 100)
519+
throw new Exception();
520+
511521
if (((IFoo<object>)new Foo<object>()).GetInterfaceType() != typeof(IFoo<object>))
512522
throw new Exception();
513523

0 commit comments

Comments
 (0)