Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Implement two pass algorithm for variant interface dispatch #21355

Merged
merged 2 commits into from
Dec 14, 2018

Conversation

MichalStrehovsky
Copy link
Member

Fixes #20452.

@MichalStrehovsky MichalStrehovsky added this to the 3.0 milestone Dec 4, 2018
@MichalStrehovsky
Copy link
Member Author

Cc @sergiy-k

Copy link
Member

@davidwrighton davidwrighton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we covered the diamond case in discussion, but not the non-diamond case. Please add handling for non-diamond scenarios.

@@ -7323,6 +7347,11 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(
{
pBestCandidateMT = candidates[i].pMT;
pBestCandidateMD = candidates[i].pMD;

// If this is a second pass lookup, we know this is a variant match. As such
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm starting to understand some of the discussions from email in the last week or so. I don't see a problem with avoiding throwing in the diamond case where there are two most specific implementations, and I believe we should pick some implementation, but I do have a concern that we should be picking between the most specific implementations, not simply picking any possible implementation.

Imagine,

interface IFoo<in T> { string M() { return "IFoo:" + typeof(T).Name; } }
interface IRequiresImpInterface<in T> : IFoo<in T> { string IFoo<T>.M() { return "IRequiresImpInterface:" + typeof(T).Name; } }

class Base {}
class Derived : Base {}

class Test : IFoo<Base>, IRequiresImpInterface<Base>
{
    static void Main()
    {
        var t = new Test();
        ((IFoo<Base>)t).M(); // Clearly calls IRequiresImpInterface<Base>.M()
        ((IFoo<Derived>)t).M(); // I think becomes order dependent in this implementation between calling IRequiresImpInterface<Base> and IFoo<Base> That doesn't seem right to me. I believe it should unambiguously call IRequiresImpInterface<Base>.M()
    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if that test above gets codified, it should have the cases in IL where class Test explicitly implements IRequiresImpInterface only, the case where it implements IFoo before IRequiresImpInterface, and the one where it implements IFoo after IRequiresImpInterface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlekseyTs please chime in if you disagree.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Only methods that implement interfaces that class explicitly claims to implement in metadata should be considered as candidates for variant interface dispatch. In the example above, method M in IFoo<Base> doesn't implement any interface that class Test implements. IFoo<T>.M in IRequiresImpInterface<Base> implements method IFoo<Base>.M for class Test. I assume implementations in base classes are handled similarly, i.e. only implementations from most derived bases are considered for variant interface dispatch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It already works that way. The loop that builds the list of candidates already kicks out those that are not most specific. In the second loop (where this comment is attached) we only go over the list of the most specific candidates to see which one is first (for the variant case) or which one is the only one that we came up with (or throw) for the non-variant case.

I've pushed out a commit that adds the test case from your comment to demonstrate we only consider the most specific ones. In that case, the variant dispatch is unambiguous because there's only one most specific implementation for it.

@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test this please

@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test

@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test this please

@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test Ubuntu arm Cross Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)

1 similar comment
@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test Ubuntu arm Cross Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)

@MichalStrehovsky
Copy link
Member Author

This keeps failing with weird Jenkinsonisms (java.lang.UnsupportedOperationException: Refusing to marshal org.kohsuke.github.GHUser for security reasons; see https://jenkins.io/redirect/class-filter/). I'll just close and reopen...

@MichalStrehovsky
Copy link
Member Author

Seems like Jenkins is not going to give up to Azure DevOps without making our lives miserable for a bit more.

@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)
@dotnet-bot test Windows_NT x64 Checked CoreFX Tests
@dotnet-bot test Windows_NT x64 Checked Innerloop Build and Test
@dotnet-bot test Windows_NT x64 Release CoreFX Tests

@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)

1 similar comment
@MichalStrehovsky
Copy link
Member Author

@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked Innerloop Build and Test
@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0)

@MichalStrehovsky MichalStrehovsky merged commit 1649da1 into dotnet:master Dec 14, 2018
@MichalStrehovsky MichalStrehovsky deleted the twopassvariance branch December 14, 2018 11:21
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants