meetings/2021/LDM-2021-09-20.md
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.
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:
D
over Action.)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.
Options 4+6 accepted.
ExpressionAnother break testing has revealed looks like this:
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.
No changes will be made.
A final break we looked at today is:
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.
No changes.
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.
Language change approved.
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.
Approved. We want to explore even more enhancements in this space.
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:
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.
Overall, we like the semantic meaning. We still need to consider whether aliases should be allowed to have top-level nullability.