# C# Language Design Meeting for June 3rd, 2024

## Agenda

- [Params collections and dynamic](#params-collections-and-dynamic)
- [Allow ref and unsafe in iterators and async](#allow-ref-and-unsafe-in-iterators-and-async)

## Discussion

### Params collections and dynamic

Issue: https://github.com/dotnet/roslyn/issues/73643

The issue describes an existing scenario where we started issuing warnings although we didn't use to. The scenario seems mainline: It's in Razor, and it combines the use of dynamic and params, both of which are common in the context. This seems an easy situation to get into.

The warning is about one or more candidates not being able to be understood by the runtime binder, which will therefore ignore them. The warning means that new overloads, in this case params collection overloads, can "poison the pot", even for cases where the runtime binder could have still handled things just fine, only with a possibly different outcome than the compile-time binder.

We do have some existing warnings of the same type, but they were grandfathered in. The problem here is that the combination of a new language feature that the runtime binder can't handle plus new overloads that use that feature cause *new* warnings in existing code.

This seems like the first of potentially several situations like this going into the future. What should our general philosophy be going forward?

If we are certain that something is not going to work out at runtime, we can give compile-time *errors*. But is there ever a case where a *warning* is the right thing for dynamic? After all, use of the feature in and of itself signals a willingness to risk things going awry at runtime.

#### Conclusion

In the particular case in question, the warning does not serve a useful purpose and is not worth the break in behavior that it causes.

Furthermore, we feel in general that warnings are not useful with dynamic. They are a signal that something *might* not work, and with dynamic people are already bought in to that outcome. All else equal we should avoid adding such warnings in the future also.

We are fine with compile-time errors when we're certain things don't work out at runtime. We're even fine adding *new* compile-time errors for *existing* scenarios that break at runtime, since people are already broken, and getting the error earlier is helpful.

### Allow ref and unsafe in iterators and async

Proposal: https://github.com/dotnet/csharplang/blob/main/proposals/ref-unsafe-in-iterators-async.md

The proposal loosens the restrictions on having ref variables and unsafe blocks in iterators and async methods. The reason for earlier restrictions is that ref variables and unsafe code need their state on the stack, and don't mix well with `yield` and `await` which need to squirrel away state in the heap. However, there is no problem with ref and unsafe being used *between* `yield`s and `await`s, as long as the ref or unsafe state does not need to be persisted across them. This is what the proposal allows.

The feature is already implemented - this is a review of the speclet to verify that we remain in agreement with all the decisions.

Some specific points of discussion:

#### Lock statements in iterators
Currently, `yield` statements are allowed inside `lock` statements in iterators. This keeps a lock while the iterator is suspended, which is highly dubious, and the proposal calls for a new warning on this pattern of code.

Additionally, there is a new variant of the `lock` statement (https://github.com/dotnet/csharplang/blob/main/proposals/lock-object.md) specifically for the `System.Threading.Lock` type. Since this type is a ref struct, such a lock statement constitutes a use of a ref across a `yield` and is therefore an error.

#### Breaking change because of Roslyn bug: 
Up until now, local functions inside unsafe async methods were erroneously allowed to have unsafe behavior, even though the top-level method body was safe due to other rules. The proposal calls for breaking this with an error going forward. Such an error is already issued for unsafe operations inside of lambda expressions - we just missed implementing it for local functions when we added those to the language.

#### Conclusion
We like this. Each design point seems well argued and balanced. We're provisionally ok with the breaking change on the local functions. We'll keep an eye on fallout and discuss if consequences turn out worse than we're able to anticipate.
