
# C# Language Design Notes for Nov 14, 2018

## Agenda

1. Base call syntax for default interface implementations
2. Switch exhaustiveness and null

## Discussion

### Default interface implementations

Example of base calls:

```C#
interface I1 { void M(); }
interface I2 { void M(); }
interface I3 : I1, I2 { void I1.M() { } void I2.M() { } }
interface I4 : I1, I2 { void I1.M() { } void I2.M() { } }
interface I5 : I3, I4
{
    void I1.M()
    {
        base<I3>(I1).M();
        base<I4>(I1).M();
    }
    void I2.M()
    {
        base<I3>(I2).M();
        base<I4>(I2).M();
    }
}
```

Q: Should we even allow the programmer to call explicit implementations?

In existing C# code this is not allowed since all explicit implementations
are private. You can always call by casting to the interface if the interface
is not re-implemented, but there's no way to do this via a base call.

However, there's no way to use overriding default interface implementation in
C# 8.0 *except* for explicit implementation, so not allowing it would basically
not allow the scenario.

Java does allow this scenario, so full compat would require us to add the feature.

Q: What type of dispatch would we use in the runtime?

One way is to constrain to the interface, then do a virtual dispatch for a
particular method. This probably would require additional (fairly expensive)
runtime work.

Q: What does the syntax need to express?

One problem is for `I3`, where by signature `M` is ambiguous because it's not
clear whether you mean `I1.M` and `I2.M`. The syntax would need to specify both
that the `I3` implementation is requested *and* whether or not to choose `I1.M` or
`I2.M`.

One other option is to just not provide a way of disambiguating `M`. If there's an
ambiguity you can't call any particular `M`.

**Conclusion**

Let's do the simplified form. If the `M` being called is ambiguous, it's illegal to
call such a method. The only configuration we're considering is which base to look
for the method.

#### Syntax

```C#
namespace N {
    interface I1<T> { int M(string s) => s.Length; }
}
class C : I1<T>
{
    int I1.M(string s)
    {
        // Options:
        base<N.I1<T>>.M(s);
        base(N.I1<T>).M(s);
        N.I1<T>.base.M(2);
        base{N.I1<T>}
        base:N.I1<T>.M(s);
        base!N.I1<T>.M(s);
        base@N.I1<T>.M(s);
        base::global::N.I1<T>.M(s);
        base.(N.I1<T>).M(s);
        (base as N.I1).M(s); // base isn't an expression, so this isn't legal today
        ((N.I1)base).M(s); // same here
        base.N.I1.M(s);  // clash with a member on base, doesn't allow for qualified name
        N.I1.M(s); // this is currently a static call on I1, can't work
    }
}
```

We think the forms that take advantage of `base` not being an expression are
too clever and would just be confusing. `N.I1.base.M(2)` is possibly tricky
for human readability since `base` is in the middle and is harder to notice
without coloration.

One problem with `base(N.I1).M(s)` is if we want to add "invocation" operators
to the language, that would disallow invoking your base.

`base::N.I1<T>.M(s)` has some history in the language.

**Conclusion**

Decided on `base(N.I1<T>).M(s)`, conceding that if we have an invocation
binding there may be problem here later on.


### Switch exhaustiveness and null

Current design: warning about not handling all inputs *except* null.

Q: What happens when the switch actually doesn't match what was given at
compile time?

**Proposal**:

A new `MatchFailureException` that extends `ArgumentException`. This allows
the compiler to use an existing exception for down-level support. There's
no data, just a message.

**Conclusion**

The base should be `InvalidOperationException`, not `ArgumentException`. If
the argument being switched on can be trivially boxed into object, we'll add
it as an argument to the `MatchFailureException`. Otherwise, just fill in
`null`.