# C# Language Design Meeting for November 3rd, 2021

## Agenda

1. [Name shadowing in local functions](#name-shadowing-in-local-functions)
2. [`params Span<T>`](#params-spant)

## Quote(s) of the Day

- "params Span<T>. This is an easy one, we should be out in 10 minutes."
- "For some value of 8"
- "Stack goes up. Stack goes down. You can't explain that."
- "That killed my monologue." "Did you mean 'evil villain monologue'?" "No one killed me during the monologue
therefore I can't be an evil villain." "No one kills the evil villain during the monologue, they just learn the evil plan and then escape to thwart it when the villain
leaves the implementation to his minions."
- "Solved the mystery: cat sitting on the spacebar"

## Discussion

### Name shadowing in local functions

https://github.com/dotnet/csharplang/discussions/5327

In C# 8, we relaxed the name shadowing requirements for lambdas and local functions, but we wanted to revisit the specific implementation strategy and whether we went too
far. In particular, we allowed a local function to _both_ capture a variable from an outer scope and shadow that variable in a nested scope inside the local function. This
_and_ in the previous sentence is the part we're concerned with: several members of the LDT didn't realize that we were agreeing to this in the original discussions on
shadowing.

There are two general models of shadowing in C#:

* Locals shadowing another local within the same function. This is expressly disallowed, and has been since C# 1.
* Locals shadowing a field. This is allowed, but the user can always get to the field version by using `this`. In the same method, it is possible to use the field without
the `this` qualifier and shadow it in a nested scope.

Shadowing between local functions while also capturing that local from the outer scope has similarities to both of these models: on the one hand, it's a local variable,
even if that variable has been potentially lifted to a field in an implicit closure. On the other hand, it's a variable from a scope outside the current function, just like
a field. This duality leads to conflicting resolution strategies, but we ultimately think this has more in common with locals shadowing other locals in the same method than
it does with locals shadowing fields.

The rules we wish we had implemented are: if a lambda or local function (or a nested lambda or local function) captures a variable from an outer method, it is an error to
shadow that variable. However, these rules have been out in the wild for nearly 3 years at this point, and we don't feel that the level of concern warrants a language change
or a warning wave to cover it. If an analyzer wants to implement such a suggestion (in dotnet/roslyn-analyzers, for example), they are free to, but the compiler itself will
not do so.

#### Conclusion

We regret that simultaneous capture/shadowing was allowed, and now it's too late to really fix it. Future features should keep this regret in mind and not use the way shadowing
in local functions/lambdas works as precedent.

### `params Span<T>`

https://github.com/dotnet/csharplang/issues/1757

`params Span<T>` is a feature that has been requested since we first implemented `Span<T>` in C# 7, and has a number of benefits. Today, users interested in performance with
`params` methods need to maintain overloads for common scenarios, such as passing 1-4 arguments (as we do for `Console.WriteLine`). These APIs are often not straightforward
to implement: if there was a simple common method they could all delegate to, that API would have been exposed in the first place.

Because of this focus, `params Span<T>` is an interesting first for the language: a `Span<T>`-based API that is explicitly targetted at the average C# developer, rather than
at someone who already knows what a `Span<T>` and why they would want to use it. There are also strong conflicting desires in the space that makes it very difficult to design
a single lowering strategy. We want to avoid allocations where possible, but we also want to avoid blowing out the stack when large numbers of parameters are passed to a
`params` method. This is particularly important in recursive code: Roslyn regularly runs into issues when inlining decisions can affect whether we are able to compile some
customer's code, as the compiler is an extremely recursive codebase.

At the same time, we also want to avoid being overly specific in the language specification on how this feature works. We would like to view the details as more an implementation
concern, much like we do with how lambdas are emitted, and avoid making strong guarantees about what will or won't stackalloc for what scenarios. As this is more targetted to
perf than lambdas are, it's possible that we can't be as glib in this space as we can for lambdas, but it's an aspiration for the feature.

Despite our desire to be less specific, we do think it's important to the initial design, and therefore the conception of who the feature is for, to have an implementation
strategy in mind. There are a number of different strategies that have been brought, with various pros and cons:

* Using a custom struct with a specific layout, and then making a span from the start of that struct.
* A shadow-stack that exists solely for the purpose of being able to give out and return stackalloc'd chunks of memory (think RAII stack space).
* Pushing variables onto the stack, then making a span from the start of those pushes (similar to the first approach, but without a dedicated type to be wrapped).
* `stackalloc` space that can be popped after a function is called.
* Not doing any kind of optimized IL in C#, and relying on the JIT to perform the escape analysis and translation from array to stackalloc when viable.

#### Conclusion

We'd like to see a group from the compiler and the runtime work through these proposed strategies and come up with "the way" or combination of ways that this will work, so we
can start to see how the user experience for the feature would actually work.
