Skip to content

Commit adb2af2

Browse files
Consider type declaration order in MethodImpls (#111998)
This fixes the Loader\classloader\InterfaceFolding\Ambiguous test. For the following program in pseudo-C# (test is in IL since this is not valid C#): ```csharp B<int, int> b = new B<int, int>(); if (!(((I<int>)b).Foo() != "B::Foo2")) { B<string, string> b2 = new B<string, string>(); if (!(((I<string>)b2).Foo() != "B::Foo2")) { Console.WriteLine("Pass"); return 100; } } Console.WriteLine("Failed!"); return -1; interface I<T> { string Foo(); } class A<U> : I<U> { string I<U>.Foo() => "A::Foo"; } internal class B<V, W> : A<V>, I<W> { string I<W>.Foo() => "B::Foo1"; string I<V>.Foo() => "B::Foo2"; } ``` We incorrectly resolve the interface call into `B::Foo1` because `I<int>.Foo` could be implemented by both `Foo1` and `Foo2` methods and we just pick whatever we see first. Per ECMA-335: "If there are multiple methods for the same interface method (i.e. with different generic type parameters), place them in the list in type declaration order of the associated interfaces." This implements the tie break.
1 parent 481eab6 commit adb2af2

File tree

1 file changed

+55
-5
lines changed

1 file changed

+55
-5
lines changed

src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,22 +277,22 @@ private static bool IsInterfaceImplementedOnType(MetadataType type, MetadataType
277277

278278
private static MethodDesc FindImplFromDeclFromMethodImpls(MetadataType type, MethodDesc decl)
279279
{
280+
if (decl.OwningType.IsInterface)
281+
return FindInterfaceImplFromDeclFromMethodImpls(type, decl);
282+
280283
MethodImplRecord[] foundMethodImpls = type.FindMethodsImplWithMatchingDeclName(decl.Name);
281284

282285
if (foundMethodImpls == null)
283286
return null;
284287

285-
bool interfaceDecl = decl.OwningType.IsInterface;
286-
287288
foreach (MethodImplRecord record in foundMethodImpls)
288289
{
289290
MethodDesc recordDecl = record.Decl;
290291

291-
if (interfaceDecl != recordDecl.OwningType.IsInterface)
292+
if (recordDecl.OwningType.IsInterface)
292293
continue;
293294

294-
if (!interfaceDecl)
295-
recordDecl = FindSlotDefiningMethodForVirtualMethod(recordDecl);
295+
recordDecl = FindSlotDefiningMethodForVirtualMethod(recordDecl);
296296

297297
if (recordDecl == decl)
298298
{
@@ -303,6 +303,56 @@ private static MethodDesc FindImplFromDeclFromMethodImpls(MetadataType type, Met
303303
return null;
304304
}
305305

306+
private static MethodDesc FindInterfaceImplFromDeclFromMethodImpls(MetadataType type, MethodDesc decl)
307+
{
308+
Debug.Assert(decl.OwningType.IsInterface);
309+
310+
MethodImplRecord[] foundMethodImpls = type.FindMethodsImplWithMatchingDeclName(decl.Name);
311+
312+
if (foundMethodImpls == null)
313+
return null;
314+
315+
// We might find more than one result due to generic parameter folding
316+
var results = new ArrayBuilder<int>(1);
317+
for (int i = 0; i < foundMethodImpls.Length; i++)
318+
{
319+
MethodDesc recordDecl = foundMethodImpls[i].Decl;
320+
if (recordDecl == decl)
321+
{
322+
results.Add(i);
323+
}
324+
}
325+
326+
if (results.Count == 0)
327+
return null;
328+
329+
int resultIndex = results[0];
330+
331+
// If we found multiple MethodImpls, need to do a tie break using type declaration order
332+
if (results.Count > 1)
333+
{
334+
MetadataType typeDefinition = (MetadataType)type.GetTypeDefinition();
335+
DefType[] interfacesOnDefinition = typeDefinition.RuntimeInterfaces;
336+
MethodImplRecord[] foundMethodImplsOnDefinition = typeDefinition.FindMethodsImplWithMatchingDeclName(decl.Name);
337+
Debug.Assert(foundMethodImplsOnDefinition.Length == foundMethodImpls.Length);
338+
339+
int bestInterfaceIndex = int.MaxValue;
340+
341+
for (int i = 0; i < results.Count; i++)
342+
{
343+
int index = Array.IndexOf(interfacesOnDefinition, foundMethodImplsOnDefinition[results[i]].Decl.OwningType);
344+
Debug.Assert(index >= 0);
345+
if (index < bestInterfaceIndex)
346+
{
347+
bestInterfaceIndex = index;
348+
resultIndex = i;
349+
}
350+
}
351+
}
352+
353+
return FindSlotDefiningMethodForVirtualMethod(foundMethodImpls[resultIndex].Body);
354+
}
355+
306356
private static bool IsInterfaceExplicitlyImplementedOnType(MetadataType type, MetadataType interfaceType)
307357
{
308358
foreach (TypeDesc iface in type.ExplicitlyImplementedInterfaces)

0 commit comments

Comments
 (0)