# C# Language Design Meeting for April 13th, 2026

## Agenda

- [Unsafe evolution, continued](#unsafe-evolution-continued)
    - [Safe markers for members with internal `unsafe` code](#safe-markers-for-members-with-internal-unsafe-code)
    - [Members with pointers in their signatures](#members-with-pointers-in-their-signatures)
    - [Choosing a temporary spelling for `safe`](#choosing-a-temporary-spelling-for-safe)

## Quote of the Day

- "The camera is so sharp it's unzipping your DNA"

## Discussion

### Unsafe evolution, continued

Champion issue: https://github.com/dotnet/csharplang/issues/9704  
Spec: https://github.com/dotnet/csharplang/blob/b040782c7f7c55cc8119620bb1b3baaf25fc419a/proposals/unsafe-evolution.md  
Working group notes: https://github.com/dotnet/csharplang/blob/b040782c7f7c55cc8119620bb1b3baaf25fc419a/meetings/working-groups/unsafe-evolution/unsafe-simple-core-model.md

Continuing from [our previous discussion](./LDM-2026-04-06.md#unsafe-evolution-continued), we returned to
the simplified core model for unsafe evolution and focused on the remaining migration and annotation questions.
We reaffirmed the overall direction from last time: `unsafe` on a member continues to introduce an unsafe
context while also making the member caller-unsafe after opt-in, and most pointer operations become safe
regardless of opt-in. The open question for today was where, if anywhere, we should require an explicit
marker that a member is still considered safe.

#### Safe markers for members with internal `unsafe` code

We first discussed whether a method that contains an `unsafe` block should be forced to carry some additional
marker indicating that the unsafety stops within the method rather than propagating to callers. The main
argument in favor is audit discipline: such a requirement forces authors to consciously decide whether a
boundary is truly safe, rather than relying on the absence of `unsafe` on the signature.

We were not persuaded that a language-level requirement was the right answer. Once the implementation is
visible, the issue is no longer an unknown boundary like `extern`; it is code we can directly inspect and
reason about. Requiring a `safe` marker on every member with internal unsafe operations felt more like ongoing
ceremony than meaningful guidance, and we preferred to leave room for analyzers or documentation to encourage
explicit safety explanations without making the language itself more repetitive.

#### Conclusion

We will not require methods with `unsafe` blocks inside them to be explicitly marked as safe.

#### Members with pointers in their signatures

We then considered a narrower migration question: even if we do not require explicit marking for all members
with internal unsafe code, should we at least require it for members whose signatures contain pointer types?
This concern came from the transition to the new model, where pointers themselves are no longer automatically
unsafe in many positions. That creates some risk that APIs which used to stand out as obviously dangerous could
become easier to overlook.

There was real sympathy for that concern, especially because pointer-taking APIs are statistically likely to do
unsafe work. At the same time, we drew a distinction between pointer signatures and opaque interop boundaries.
For ordinary members, the actual implementation remains available for inspection, and the real question is what
operations the body performs, not merely whether a pointer appears in the signature. We therefore did not want
to special-case all such members in the language itself just because they look suspicious.

We still think tooling can help here. Analyzers and migration guidance can encourage review of pointer-heavy
APIs without baking a separate rule into the language.

#### Conclusion

We will not require methods with pointers in their signatures to be explicitly marked as safe.

#### Choosing a temporary spelling for `safe`

With the broader annotation questions resolved, we returned to the remaining case from earlier meetings:
`extern` and similar members, where we still want some explicit way to indicate that a declaration has been
reviewed and found safe despite crossing a boundary the compiler cannot analyze.

The main design question here was whether that indication should be a `safe` keyword or an attribute. The case
for a keyword was long-term regularity if the language eventually grows more explicit safe/unsafe constructs.
The case for an attribute was that this is a rare, asymmetric exception and not a broadly used language concept.
The room leaned one way, but we did not have enough time left in the meeting to fully explore the topic, and
the preference was not strong enough for us to treat the choice as permanently settled.

#### Conclusion

As a temporary decision, we will use the `safe` keyword to keep the design moving, and will revisit that
choice in a future meeting when we have time to get into a deeper discussion.
