# C# Language Design Meeting for September 20th, 2021

## Agenda

1. [Lambda breaking changes](#lambda-breaking-changes)
2. [Newlines in non-verbatim interpolated strings](#newlines-in-non-verbatim-interpolated-strings)
3. [Object initializer event hookup](#object-initializer-event-hookup)
4. [Type alias improvements](#type-alias-improvements)

## Quote of the Day

- "You're kicking in an open door"

## Discussion

### Lambda breaking changes

https://github.com/dotnet/roslyn/pull/56341

In the ever continuing saga of new breaking changes introduced by giving method groups and lambda expressions natural types, we looked
at a few new breaking changes today to decide what, if any, workarounds we should adopt to try and fix them.

#### Breaking changes around overload resolution

https://github.com/dotnet/roslyn/issues/55691  
https://github.com/dotnet/roslyn/issues/56167  
https://github.com/dotnet/roslyn/issues/56319  
https://github.com/dotnet/csharplang/discussions/5157  

We have a number of reports from users who have been broken by changes in overload resolution, mostly because a set of overloads that
used to succeed in overload resolution are now ambiguous. A smaller group met to discuss a number of different potential solutions to
the issue. These options were:

1. Leave the breaking changes as-is.
2. Change “method type inference” and “best common type” to not infer from the natural type of a lambda expression or method group.
3. Change “better function member” to treat delegate types with identical signatures as equivalent, allowing tie-breaking rules to apply.
4. Change “better function member” to prefer overloads where method type inference did not infer type arguments from the natural types
of lambdas or method groups.
5. Change “better function member” to prefer parameter types that are delegate types other than those used for natural type. (Prefer `D`
over `Action`.)
6. Change “better function member” to prefer argument conversions other than “function type” conversions.
7. Change “better function member” to prefer parameter types `D` or `Expression<D>` over `Delegate` or `Expression`, where `D` is a delegate type.

Discussion further narrowed our focus to two combinations of the above options: 3+7 or 4+6. 3+7 results in a more aggressive break, while
4+6 is more compatible with previous versions of C#. Given the extent of some of the breaks we're seeing, we think the more compatible
approach is the better way to go, so we'll proceed with the PR linked at the start of this section.

##### Conclusion

Options 4+6 accepted.

#### Method groups converting to `Expression`

Another break testing has revealed looks like this:

```cs
var c = new C();
c.M(F); // C#9: E.M(); C#10: error CS0428: Cannot convert method group 'F' to 'Expression'.

static int F() => 0;

class C
{
    public void M(Expression e) { Console.WriteLine("C.M"); }
}
static class E
{
    public static void M(this object o, Func<int> a) { Console.WriteLine("E.M"); }
}
```

We think we would have a solution for this: split our "function type conversion" into two separate conversion types: a function type
conversion from lambda, and a function type conversion from method group. Only the former would have a conversion to Expression. This
would make it so that `M(Expression)` is not applicable if the user passed a method group, leaving only `M(object, Func<int>)`. This
could be a bit complex, but it should resolve the issue.

Unlike the previous examples, however, we don't have any reports of this issue. Given the number of reports of the previous breakages
we've received, and the lack of reports for this issue, we tentatively think that it's not worth fixing currently. If, after we ship
C# 10 for real, we received reports of this break, we know how to fix it and can change course at that time without making a breaking
change.

##### Conclusion

No changes will be made.

#### Lambdas in OHI

A final break we looked at today is:

```cs
using System;

B.F1(() => 1); // C#9: A.F1(); C#10: B.F1()
var b = new B();
b.F2(() => 2); // C#9: A.F2(); C#10: B.F2()

class A
{
    public static void F1(Func<int> f) { }
    public void F2(Func<int> f) { }
}
class B : A
{
    public static void F1(Delegate d) { }
    public void F2(Delegate d) { }
}
```

This is standard OHI behavior in C#, but because the derived overloads were previously not applicable, they were not included in the
`B.F1` or `b.F2` method groups, and only the methods from `A` would be applicable. Now that methods from the more derived type are
applicable, methods from the base type are filtered out by method group resolution.

We think this is both fine and actually desirable behavior. We don't have contravariant parameters in C#, but this is effectively
acting like such, which is a good thing. This change is also not customer-reported, but was instead discovered in testing. Given the
desirable behavior and lack of reports, we think no change is necessary.

##### Conclusion

No changes.

### Newlines in non-verbatim interpolated strings

https://github.com/dotnet/csharplang/issues/4935

We have a lot of compiler complexity around ensuring interpolated strings do not have a newline in them, and we don't see a real
reason to forbid newlines. We think the origin might have come from the number of different design flip-flops we made on interpolated
strings during their initial design.

#### Conclusion

Language change approved.

### Object initializer event hookup

https://github.com/dotnet/csharplang/issues/5176

LDM is not only interested in this change, we're also interested in generalized improvements that can be made in object initializers
and with expressions. Compound assignment is interesting, particularly in `with` expressions, and we would like to see what improvements
we could make not just for events, but for all types of properties and fields.

#### Conclusion

Approved. We want to explore even more enhancements in this space.

### Type alias improvements

https://github.com/dotnet/csharplang/issues/4284

Finally today, we looked at one of the open questions in this proposal: how should we handle nullable types in using aliases when the
alias is used in a `#nullable disable` location.

There are largely 2 ways to view using aliases:

1. Syntactic substitutions: the compiler is literally copy/pasting the thing in the alias into the target location. In this view, the
compiler should treat the syntax as occuring at the use point, and warn based on that.
2. Semantic substitutions: the using alias is effectively defining a new type. It's not a truly different type, but only the meaning
is substituted, not the actual syntax. If we ever want to consider a way to export using aliases, this will be a useful meaning to assume.

We also have some (possibly unintended) prior art here: `using MyList = System.Collections.Generic.List<string?>;` takes the second
approach today, acting like a semantic substitution.

The one thing we still want to consider in this space is top-level nullability. We're not sure about allowing a type alias to have
top-level nullability when it's an alias to a reference type. There is (very intentionally) no extra C# syntax for "not null reference
type" beyond the lack of a `?`, and the next ask if we were to allow aliases to be top-level nullable would be for such a syntax.

#### Conclusion

Overall, we like the semantic meaning. We still need to consider whether aliases should be allowed to have top-level nullability.
