meetings/2025/LDM-2025-11-12.md
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:
unsafe type as unsafe. This is the current proposal.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.unsafe on types.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.
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.