Back to Csharplang

C# Language Design Meeting for September 24th, 2025

meetings/2025/LDM-2025-09-24.md

latest5.2 KB
Original Source

C# Language Design Meeting for September 24th, 2025

Agenda

Quote(s) of the Day

  • "You'll all choke on your own bile and say that can't possibly be the solution"
  • "I got a head start on showing [redacted] that they're wrong. You know, I gotta get my hand up there and make sure I'm first in line."
  • "Let's get a yummy rare steak in the ground"

Discussion

Union Syntax Thunderdome Part 1

Champion issue: https://github.com/dotnet/csharplang/issues/9662
Specification: https://github.com/dotnet/csharplang/blob/38fd5f33d285cb190268f98cea16223cc0a5b8bc/proposals/unions.md
Proposals:

Today we begin the syntax thunderdome for unions: while we don't expect to make every decision today (and spoiler alert, we did not), we need to make enough progress today and in the next couple of meetings for the compiler team to start implementation without later needing to significantly redesign the parsing stages. We have a few different options on the table, and we started to break them down by philosophies. Specifically, we have 2 main flavors of union here: a union that states that its cases are pre-existing types, and a union that states that its cases are types that should be generated inline. One major question, therefore, is whether or not we expect these different flavors to mix; do we expect users to often declare intermixed unions of both pre-existing types and new types, or do we expect it to be homogenous. And, if we often expect it to be homogenous, do we really need a syntax that allows both flavors in a single union declaration? One key insight is that the union of newly created types is just sugar over the union of pre-existing types; we can likely formalize the former as literally desugaring into the latter, with the new types declared as nested types within the union. This then potentially allows us to nicely relate this back to enums; we can say that enums are for declaring new cases (whether that's numeric cases or types cases), and union is specifically for bundling a group of existing types. We could then potentially allow a grow-up story for enums where they can add new types of members. Some difficult hurdles remain in that area though:

  • How does the language decide whether an enum is number-based or type-based? Is the difference just in whether any of the cases has nested values? Is that too subtle?
    • Other languages do use just this nested value presence in similar fashions, like Swift and Rust, but neither are quite like C# enums.
    • Could we perhaps use a modifier like enum class or enum struct to convey this difference?
  • How would such enums interact with System.Enum? Would we change the runtime to allow them to inherit from System.Enum? Would that be too large of a breaking change?
    • If they don't interact with System.Enum, is that too large a divergence to continue using the enum keyword?

We do think that this idea has some merit and needs to be explored, and several LDM members plan to do just that ahead of Monday's meeting. We also took some initial reads of the room on the basic building blocks of unions. So far, LDM leans towards unions not having a dedicated shorthand syntax for declaring new types; they are made up of existing types. We are not ruling out the separate enum of types idea that would desugar into a union with nested declarations, but to use an analogy to classes and records, unions would be the more general building block on which a potential enum of types feature would build, just as classes are the building block on which records build.

We also thought about whether the cases of unions should be inside or outside the curly braces of the body. Again, there are no easy options here. If they are outside the curly braces, our precedent for scoping visibility suggests that any nested types from inside the union are not visible without being qualified by the type of the union, including any generic type arguments. However, in order to implement closed type hierarchies, we may need to have a list of the allowed implementations, similar to Java's permits clause, both for compiler performance and for reader comprehension. If we do need to have such a clause for that case, it might be a good idea to unify that with unions. On the other hand, unions could also reuse the enum syntax; again, if we expect to allow other member types to be defined in an enum, we will need to solve the syntax question of both listing the cases and other members in the body of the enum, and union could potentially reuse that same syntax. Subjectively, we do have a slight leaning that by writing the members inside the body, rather than at the declaration level, it implies that they are new things, rather than existing things. Given that, we have a very slight lean towards outside the body of the union, but will explore more proposals on Monday and hopefully make further concrete progress on this very gnarly question.