Why does a zero-length stackalloc make the C# compiler happy to allow conditional stackallocs?

The Span<T> / ref feature is essentially a series of rules about to which scope a given value can escape by value or by reference. While this is written in terms of method scopes it’s helpful to simplify to just one of two statements:

  1. The value cannot be returned from the method
  2. The value can be returned from the method

The span safety doc goes into great detail about how the scope is calculated for various statements and expressions. The relevant part here though is for how locals are processed.

The main take away is that whether or not a local can return is calculated at the local declaration time. At the point the local is declared the compiler examines the initializer and makes a decision about whether the local can or cannot be return from the method. In the case there is an initializer then the local will be able to return if the initialization expression is able to be returned.

How do you handle the case where a local is declared but there is no initializer? The compiler has to make a decision: can it or can it not return? When designing the feature we made the decision that the default would be “it can be returned” because it’s the decision that caused the least amount of friction for existing patterns.

That did leave us with the problem of how developers could declare a local that wasn’t safe to return but also lacked an initializer. Eventually we settled on the pattern of = stackalloc [0]. This is an expression that is safe to optimize away and a strong indicator, basically a requirement, that the local isn’t safe to return.

Knowing that this explains the behavior you are seeing:

  • Span<int> s = stackalloc[0]: this is not safe to return hence the later stackalloc succeeds
  • Span<int> s = default: this is safe to return because default is safe to return. This means the later stackalloc fails because you’re assigning a value that isn’t safe to return to a local that is marked as safe to return
  • Span<int> s;: this is safe to return because that is the default for unininitialized locals. This means the later stackalloc fails because you’re assigning a value that isn’t safe to return to a local that is marked as safe to return

The real downside to the = stackalloc[0] approach is that it’s only applicable to Span<T>. It’s not a general solution for ref struct. In practice though it’s not as much of a problem for other types. There is some speculation on how we could make it more general but not enough evidence to justify doing it at this point.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)