# C# Language Design Meeting for October 29th, 2025

## Agenda

- [Unsafe evolution](#unsafe-evolution)

## Quote of the Day

I did not record any particularly amusing quotes today, sorry

## Discussion

### Unsafe evolution

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

Today, we started reviewing the initial specification for evolving `unsafe` at the C# level. We spent most of the session simply
getting clarity on what is being proposed, the impacts to code, and the limits of what can be detected by the compiler. Of
particular interest was the boundary between safe and unsafe code: can we strengthen guarantees about what must be in an `unsafe`
block? We considered this hypothetical bug:

```cs
int* ptr = stackalloc int[] { 1, 2, };
int addr = 0;
AddOne(ref addr);
unsafe
{
    Console.WriteLine(ptr[addr]);
}

void AddOne(ref int i)
{
    i++;
    i++; // Oops, someone accidentally duplicated a line
}
```

If that bug were checked in, the memory unsafety occurs in the `unsafe` section, but the true bug that _caused_ the unsafety
occurred in C# that has no reason to be marked as `unsafe`, and no part of the calculation that was in error is required to be
inside the `unsafe` block. Could there perhaps be an expanding rule, such that any inputs to an `unsafe` operation must also be
in an `unsafe` block? We ultimately don't think this expansion is worth it; in general, we do not expect that it will be possible
to detect every single input that is ultimately unsafe. A counterexample is something like:

```cs
void ReadFromArrayWithOffset(int[] array, int addr)
{
    if (addr + 1 >= array.Length)
    {
        throw new ArgumentOutOfRangeException();
    }
    AddOne(ref addr);
    fixed (int* ptr = array)
    {
        unsafe
        {
            Console.WriteLine(ptr[addr]);
        }
    }
}
```

How would the compiler be able to detect that, in fact, `addr` (up until the `AddOne` call) was actually perfectly safe and valid?
It is an input to the unsafe operation, so virality would likely demand that `ReadFromArrayWithOffset` be declared as `unsafe`.
Aliasing rules further complicate this. The goal of the `unsafe` feature is to better call out what parts of the code need more
manual scrutiny, but they ultimately don't _replace_ that scrutiny. We think it is the job of the reviewers looking at this code
to understand what is really involved in the calculations and enforce `unsafe` boundaries appropriately here.

We made no conclusions today, but have a better understanding of where the boundaries of the feature are and what the principles
we will be using to make further decisions are. We will come back next week to continue work here.
