Skip to content

Invalid method is chosen by runtime as an interface implementation in presence of Default Interface Methods #43340

Closed
@AlekseyTs

Description

@AlekseyTs

Compile and run the following program (https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxAgrgOyQExAagB8ABAJgAJiBGAdgFgAoAb0fLcqoDZKAWcgWQgBLTAAoAlK3YsG7OeQAqcWKMxwA7uQDCE8QG4p8xcpiqN20roOz2AX0PkH1bsT5KVASQCC5APaSbNhkjNmoAOmoATlFfMP44GAALXxwqKwc5cKiYuITknEtxfQd7BlLGEXgEADMIAGM4cm9GYPZiAGYOAAYBPJS08XIAXgA+cgAib1yk/vHrTM7qHviZgolhscmvafzSOcZyhjJyHxAmnyZyQ4hgWAR6mEoKACFyM4urxkZjrTfyZ5aTkWVGWfVS61GEy0O1m8zanQAbkIEDB0BAADbdXqrQobKEwgr7MpfI4ULwUM7NZhA8hIlFozFLbH5AZ48bkglUIkLWnI1EYrErXYQzYcoUpPbWa63GD3OqPY7PCknCiXQ4/ZVKwGBchqTQdQVg1mQ8ZaUic7nsPWURF8hmGnEiqHm8WEqWMIA==):

class Program
{
    static void Main()
    {
        Test(new C());
        Test(new C2());
    }
    
    static void Test(IA o)
    {
        System.Console.WriteLine(o.Method1());
        System.Console.WriteLine(o.Method2());
    }
}

interface IA
{
    public string Method1() => "IA.Method1";
    public string Method2() => "IA.Method2";
}

class A : IA { }

abstract class B : A { }

class C : B
{
    public string Method1() => "C.Method1";
    public virtual string Method2() => "C.Method2";
}

class A2 : IA
{
    public virtual string Method1() => "A2.Method1";
    public virtual string Method2() => "A2.Method2";
}

abstract class B2 : A2 { }

class C2 : B2
{
    new public string Method1() => "C2.Method1";
    new public virtual string Method2() => "C2.Method2";
}

Observed:

IA.Method1
C.Method2
A2.Method1
A2.Method2

Expected:

IA.Method1
IA.Method2
A2.Method1
A2.Method2

Note, the class C is at the bottom of inheritance hierarchy. It doesn't claim to extend the interface in IL and none of the methods in it claim to explicitly implement a method from the interface:

.class private auto ansi beforefieldinit C
    extends B
{
    // Methods
    .method public hidebysig 
        instance string Method1 () cil managed 
    {
        // Method begins at RVA 0x20a4
        // Code size 6 (0x6)
        .maxstack 8

        IL_0000: ldstr "C.Method1"
        IL_0005: ret
    } // end of method C::Method1

    .method public hidebysig newslot virtual 
        instance string Method2 () cil managed 
    {
        // Method begins at RVA 0x20ab
        // Code size 6 (0x6)
        .maxstack 8

        IL_0000: ldstr "C.Method2"
        IL_0005: ret
    } // end of method C::Method2

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x20b2
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void B::.ctor()
        IL_0006: nop
        IL_0007: ret
    } // end of method C::.ctor

} // end of class C

It feels unexpected that C.Method2 is considered by runtime as a candidate to implement IA.Method2. Presence of DIM should make no difference here, I think.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions