Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish meeting notes "2017-03-23 CLR Behavior for Default Interface Methods" #404

Closed
gafter opened this issue Apr 4, 2017 · 3 comments
Closed
Assignees

Comments

@gafter
Copy link
Member

gafter commented Apr 4, 2017

The following meeting notes need to be added to the design notes section of the repository.


2017-03-23 CLR Behavior for Default Interface Methods

Met today with

To discuss the CLR handling of default interface methods.

(1) Runtime handling of ambiguities

Question (1) is about what the CLR should do when the runtime does not find a most specific override for an interface method. For example, if this program is compiled (each interface being in a separate assembly)

public interface IA
{
    void M();
}
public interface IB : IA
{
    override void IA.M() { WriteLine("IB"); }
}
public interface IC : IA
{
}
class C : IB, IC
{
    static void Main()
    {
        IA a = new C();
        a.M();
    }
}

and then subsequently IC is changed

public interface IC : IA
{
    override void IA.M() { WriteLine("IB"); }
}

This would cause an error at compile-time if class C were recompiled (because there is no most specific override for IA.M). However, what happens if this program is run without recompiling C?

There are two most choices:

  1. The runtime selects, deterministically, between the implementations of IA.M in IB and IC; or
  2. The runtime fails at some time before this invocation is run.
    a. At load time; or
    b. At the time of the invocation

We did not have consensus which approach is preferred; there are advantages and disadvantages to either. We will defer this issue until it can be discussed in a larger group.

Static methods

Static methods are already permitted in interfaces.

Protection levels

From the point of view of the CLR, once we start accepting a protection level other than private, we might as well accept all of them. The semantics are clear.

With the possible exception of protected and its related protection levels. We would need to clearly define both the language and runtime constraints implied by this protection level. For example, is a protected member in an interface accessible in a class that implements the interface? What would be the syntax in source (note that interface members are not inherited into classes that implement them)? What would be the verifiable form of the generated IL?

We tentatively agreed that the CLR will accept protection levels other than public in interfaces, but open questions remain about protected.

Sealed override

It is an open question whether a sealed override should be permitted within an interface. Given the most specific override rule, such code could prevent unrelated sister interfaces from implementing the method.

If it were permitted, we need to check that there is a way to represent it in IL.

Non-virtual methods in interfaces

Non-virtual methods in interfaces are not a problem for the CLR, presuming we have a way to represent them. We need to check that existing interface methods have a newslot bit set. We think so. If so, then we would simply omit that from non-virtual methods.

Implicit overrides in interfaces

We agree that the compiler-generated IL for an "implicit" override

public interface IA
{
    void M();
}
public interface IB : IA
{
    override void M() { WriteLine("IB"); }
}

should be roughly the same as for an "explicit" override

public interface IB : IA
{
    override void IA.M() { WriteLine("IB"); }
}

In both cases the compiler produces a methodimpl record for the overridden method(s). The CLR will not consider a method in an interface to be an implementation of another method without the methodimpl bit. We should confirm that we are OK with the binary compatibility implications of this.

All override declarations should omit the newslot bit to ensure no new vtable slot is allocated.

Open Question: Which interfaces are searched

There is an open question which interfaces are searched for implementations, and in which order. For example, if we have

interface IA { void M() {} }
interface IB: IA { override void M() {} }

class Base : IB { }
class Derived : Base, IA { }

Then is the override appearing in IB the final override selected for class Derived, even though IB does not appear in the interface list for Derived?

Open Question: Diamond Inheritance between Class and Interface

What should occur in cases involving diamond inheritance with classes and interfaces:

interface IA { void M() {} }
interface IB: IA { override void M() {} }

class Base : IA { void IA.M() { } }
class Derived : Base, IB { }

In this case neither Base nor IB is a subtype of the other. Is class Derived legal? If so, what is its implementation of IA.M?

@gafter gafter self-assigned this Apr 4, 2017
@HaloFour
Copy link
Contributor

HaloFour commented Apr 4, 2017

The runtime fails at some time before this invocation is run.

I will argue that this will defeat one of the major tenets of this entire feature, which is the ability to evolve interfaces without causing breaking changes to existing compiled assemblies. Would the BCL ever consider adding new default members knowing that they might accidentally create an ambiguity with some interface defined in some other assembly? Obviously not. Therefore, the BCL could never evolve any of their own interfaces.

@gafter
Copy link
Member Author

gafter commented Apr 4, 2017

@HaloFour While I agree with you, we need to ensure we reach consensus on whatever approach we take. The BCL authors and maintainers will be key to this decision.

@gafter
Copy link
Member Author

gafter commented Apr 5, 2017

This has now been documented in #407.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants