Back to Csharplang

C# Language Design Meeting for December 17th, 2025

meetings/2025/LDM-2025-12-17.md

latest5.8 KB
Original Source

C# Language Design Meeting for December 17th, 2025

Agenda

Quote of the Day

  • "We can't do advent of triage? Just one more?"

Discussion

Collection expression arguments parsing question

Champion issue: https://github.com/dotnet/csharplang/issues/8887
Spec: https://github.com/dotnet/csharplang/blob/01e436bf45790b7118c116100c27a8172148c58d/proposals/collection-expression-arguments.md#finalizing-an-open-concern-from-httpsgithubcomdotnetcsharplangblobmainmeetings2025ldm-2025-03-17mdconclusion

We started today by revisiting an earlier temporary decision; when we were considering whether with would be a breaking change at the top level for collection expressions, we initially decided that we would take the breaking change for preview, but since we hadn't yet finalized our syntax and were planning on previewing it for .NET 10, we didn't want to potentially break users for syntax we weren't certain of. Since then, we've had our debates, settled on with as the syntax, and now we want to revisit the breaking change downlevel. Our default approach to parsing changes is to always parse with new language features, and issue errors when users don't have the appropriate language version enabled. Searching through GitHub we are unable to find examples of code that will actually break in this, so we are comfortable following our usual approach; the .NET 11 SDK/C# 15 compiler will always parse with as collection expression arguments, regardless of the language version of the project.

We also wanted to talk about the formal definition of the feature, and in particular, how the grammar rules actually will work. Do we want to have a rule where the with has to be the top-level expression in the collection expression element, or do we want a rule where, if we encounter with( to start a collection expression element, that element is a with element, regardless of following content? Concretely, the question is how do we want something like this to parse: [with() + with()]. With the top-level rule, this would just be a binary operator + with method calls to methods named with in either arm. With the "first token" rule, it would be a with element, followed by a binary operator + whose left operand is missing, and the right operand is a method invocation named with. After some debate, we decided that it is simpler to go with the "first token" rule, and matches our precedent with spread elements, where a .. always starts a spread, never a range. This makes the parsing simple, and parentheses can easily disambiguate for scenarios where the user really wants to call a method, just like it works for range.

Conclusion

We will not take language version into account when parsing, and will always parse with elements as with elements. If a collection expression element starts with with(, that means it is a with element, following the .. spread precedent.

Null-conditionals evaluating to a pointer type

Issue: https://github.com/dotnet/roslyn/issues/7502
Spec: https://github.com/dotnet/csharpstandard/blob/41694caebb0fb759b75ef66b9d11d37006aab000/standard/expressions.md#12813-null-conditional-element-access

Next up, we discussed a potential compiler bug/specification bug. This has been open for a long time, but was recently resurfaced and we wanted to investigate to clarify our definitions. The question hinges on what the definition of what a non-nullable value type is. If a pointer is a non-nullable value type, then the rules as written state that the type would have to be Nullable<int*>, which is indeed illegal. But if pointers are not non-nullable value types, then the cases would fall through to the last bullet of the spec and it should just work, as the specified transformation would be legal for a pointer. Certainly on the surface, pointer types are nullable: int* i = null; is legal, as is comparing them with null. Given that, and the definitions in §8.2.1, we are comfortable treating this as a compiler bug, and will fix it to work. We will also leave the decision on clarifying the specification further to the ECMA 334 committee.

Conclusion

Null-conditional expressions that produce a pointer type not being allowed is a compiler bug and will be fixed as such.

Triage

Finally today, we did some triage on newly-championed issues over the past few months.

Top-Level Members

Champion issue: https://github.com/dotnet/csharplang/issues/9803
Spec: https://github.com/dotnet/csharplang/blob/e878e7860d9b6420112fd7b99b910a79c7103132/proposals/top-level-members.md

There are some strong feelings on this one in the LDM; nothing that we think would hold us from doing a deep design dive on it, but by no means is there agreement on the feature at this point. We'll put it into the working set to have that debate.

Using local declaration improvements

Champion issue: https://github.com/dotnet/csharplang/issues/8606
Spec: https://github.com/dotnet/csharplang/blob/afad49721a7b9923efbbc36ae6c455d85b994543/proposals/using-with-a-discard.md

Finally, we looked at a proposal for creating using local declarations without actually having to declare a local, marrying the implicit scope benefits of using locals with the lack of variable pollution available from using statements. We are interested, but want to rename the proposal from the current state to better reflect that goal, rather than a specific implementation of achieving that goal.