Back to Csharplang

C# Language Design Meeting for November 12th, 2025

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

latest4.0 KB
Original Source

C# Language Design Meeting for November 12th, 2025

Agenda

Quote of the Day

  • "Did you just say Romulan?" "No, I said cromulent."

Discussion

Unsafe evolution

Champion issue: https://github.com/dotnet/csharplang/issues/9704
Specification: https://github.com/dotnet/csharplang/blob/4c6bee6ceaa48dc64a42f25d6a50155e1def2930/proposals/unsafe-evolution.md
Related: https://github.com/dotnet/designs/blob/2b9e1b311d16ea142b4047d6eddeef15979ba6de/proposed/caller-unsafe.md

Today, we continued talking about the future of unsafe in C#. Our main focus today was on how members are declared as requiring an unsafe context: do we repurpose the unsafe keyword on members to mean this? Or do we do something new, such as a new keyword or having an attribute to indicate this? After some discussion, we do believe that unsafe conveys what we want here. Much like nullable reference types, the future we want to get to isn't with the unsafe keyword as this odd vestigial thing, we want the feature to feel unified. Given that we feel pretty confident that we want to repurpose unsafe on members, even if that means increasing the scope of the breaking change in the language.

Next, we considered the broader application of unsafe on types. The new rules are very explicit that types cannot be considered unsafe by nature: it is only specific types of memory accesses that are unsafe. We therefore have a few options for what to do:

  1. Automatically mark all members inside an unsafe type as unsafe. This is the current proposal.
  2. Keep the historical meaning of unsafe: the body of the type is in an unsafe context, but members will still need a separate unsafe modifier to be considered unsafe to call.
  3. Disallow unsafe on types.
  4. Allow unsafe as a modifier on types, but remove all meaning. Produce a warning that it is vestigial.

All of these are fairly large changes in one way or another; a simple search across GitHub indicates at least 150K results for unsafe class or unsafe struct, excluding forked projects. A decent number of spot checks of these have shown that they're not actually unsafe; the developer simply marked the type as unsafe even though no pointers or unsafe memory accesses occurred in the type. There are plenty of real unsafe spots, though, so unless we were to adopt option 2, it would be a large breaking change.

Looking more at options 1 and 2 though, we're not actually convinced that these truly help bring down the magnitude of the breaking change. In either case, developers really do still need to adjust their members. Option 1 will bleed unsafe out into the rest of the code, and both options 1 and 2 make the scope of the unsafe blocks far too broad. In both cases, we don't actually want users to put unsafe on their types. It implies that a type can be unsafe, which is not how the feature works. We are then left with options 3 and 4, where 4 is just 3 but without a hard error. There's definitely appetite for fully prohibiting unsafe on types altogether, but we're hesitant to fully commit at this time. We'll proceed with option 4 for now, and come back to the question of 3 vs 4 in a few weeks after we've had some time to think more about it.

Another area we considered is whether or not unsafe as a modifier on a member should make the entire body of that member an unsafe context. Rust did this originally, and has since moved to requiring an explicit unsafe block, even for a member marked as unsafe. This helps call out the specific unsafety, rather than saying "this entire 100 line method is unsafe, good luck finding the actual bit of concern". We don't have conclusions on this today, but will add it to our list of questions to resolve.

Conclusion

We will use unsafe on members as the way to denote that calling a member must be performed in an unsafe context. unsafe on a type will have no meaning, and we will revisit the question of whether to outright disallow it, or just add a warning.