More unsafe spec updates#9831
Conversation
Add a few more open questions and adjust the wording with decisions from the [12th](https://github.com/dotnet/csharplang/blob/5d9c7bec7677a6ce335652712b9fe483680ef487/meetings/2025/LDM-2025-11-12.md).
| @@ -136,7 +136,13 @@ it would be if any parameter were a by-`ref`, optional, or `params`. | |||
|
|
|||
| It is a memory safety error convert a delegate type that is marked as `unsafe` to `System.Delegate`/`System.Linq.Expressions.Expression`/`System.Linq.Expressions.Expression<T>`. | |||
There was a problem hiding this comment.
Should this include object as well? Otherwise, one can do unsafe delegate -> object -> safe delegate.
What can one actually do with the unsafe delegates with all these limitations? #Resolved
There was a problem hiding this comment.
What can one actually do with the unsafe delegates with all these limitations?
Pass them around and call them in an unsafe context. I don't know how much people are generally counting on converting them object or similar; I certainly don't use that functionality.
There was a problem hiding this comment.
Right, pass them around on your little island. I do not expect libraries are generally going to have APIs that take unsafe delegate arguments.
To me, it feels like that the unsafe delegates are a mine field that will require a lot of special casing and provide very little value. Starting without them may be better.
| ### Delegate type `unsafe`ty | ||
|
|
||
| We could remove the ability to make delegate types as `unsafe` entirely, and simply require that all conversions of `unsafe` lambdas or method groups to a delegate type occur inside an `unsafe` context. | ||
| This could simplify the model around `unsafe` in C#, but at the risk of forcing `unsafe` annotations in the wrong spot and having an area where the real area of `unsafe`ty isn't properly called out. |
There was a problem hiding this comment.
If we started without unsafe delegates, can they be added later as an incremental non-breaking change once/if proven necessary? #Resolved
| it would be if any parameter were a by-`ref`, optional, or `params`. | ||
|
|
||
| It is a memory safety error convert a delegate type that is marked as `unsafe` to `System.Delegate`/`System.Linq.Expressions.Expression`/`System.Linq.Expressions.Expression<T>`. | ||
| It is a memory safety error convert a delegate type that is marked as `unsafe` to `System.Delegate`/`System.Linq.Expressions.Expression`/`System.Linq.Expressions.Expression<T>`, or any interface those |
There was a problem hiding this comment.
Does it mean that the unsafe delegates cannot be used as generic arguments (in safe contexts)?
If they can be used as generic arguments, one can do the offending memory unsafe cast indirectly by doing e..g.:
IEnumerable<UnsafeDelegate> e1 = ...!;
IEnumerable<Delegate> e2 = e1.Cast<Delegate>();
There was a problem hiding this comment.
Would it be better to postpone the whole unsafe delegate idea until we have evidence that it is actually worth it to deal with all the complex corner cases that it comes with?
| > [!NOTE] | ||
| > We don't actually attribute the delegate type itself, just the `Invoke`, `BeginInvoke`, and `EndInvoke` methods. Determining whether a delegate type is `unsafe` is done by examining those 3 methods. | ||
| > If all are marked as `unsafe`, the delegate type is considered `unsafe`. If only some are marked as `unsafe`, then it is presumed that calling the others is safe and only calling the member that is | ||
| > marked as `unsafe` will cause a memory safety error. It will be a memory safety error to convert an `unsafe` lambda or method group to a delegate type that is does not have all of `Invoke`, `BeginInvoke`, |
There was a problem hiding this comment.
| > marked as `unsafe` will cause a memory safety error. It will be a memory safety error to convert an `unsafe` lambda or method group to a delegate type that is does not have all of `Invoke`, `BeginInvoke`, | |
| > marked as `unsafe` will cause a memory safety error. It will be a memory safety error to convert an `unsafe` lambda or method group to a delegate type that does not have all of `Invoke`, `BeginInvoke`, |
| member bodies of that type are considered an `unsafe` context. `unsafe` on a member will mean that that member is `unsafe`, and the body of that member is considered an `unsafe` context. For existing code | ||
| moving to the new definition of `unsafe`, this may produce a number of false positives for methods that don't need to be considered `unsafe`; we believe this better than false positives around not doing | ||
| this, or making it an error to put `unsafe` on a type which would easily be the largest breaking change that we've ever introduced in C#. | ||
| (except for iterator bodies). We propose changing this definition from textual to sematic. `unsafe` on a member will mean that that member is `unsafe`, and the body of that member is considered an `unsafe` |
There was a problem hiding this comment.
Regarding the syntactical meaning of unsafe on bodies, there is no change, right? So for example, iterators still escape out of the unsafe context.
There was a problem hiding this comment.
There's an open question on that.
There was a problem hiding this comment.
There's an open question on that.
|
|
||
| ### Lambda/method group natural types | ||
|
|
||
| Today, the only real impact on codegen (besides additional metadata) is changing the *function_type* of a lambda or method group when `unsafe` is in the signature. If we were to avoid doing this, then |
There was a problem hiding this comment.
This is not impacting just codegen, it's impacting semantics too since different delegate types cannot be converted to each other, so for example this snippet will stop working:
unsafe void M() { }
var x = M;
System.Action y = x;|
@333fred can this be merged? |
Test plan: #81207 OHI not handled as part of this PR. Delegates handled according to this open question: https://github.com/dotnet/csharplang/blob/79da45137622071d3c80ebe0a1296387af67afb9/proposals/unsafe-evolution.md#delegate-type-unsafety (i.e., it's not possible to mark delegates as caller-unsafe - I'd expect this to be approved and even if not, it seems like a good starting point - see also comments by jkotas under dotnet/csharplang#9831).
Add a few more open questions and adjust the wording with decisions from the 12th.