Back to Csharplang

C# Language Design Meeting for October 29th, 2025

meetings/2025/LDM-2025-10-29.md

latest2.9 KB
Original Source

C# Language Design Meeting for October 29th, 2025

Agenda

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.