[Proposal]: Change ref safety errors to warnings in unsafe contexts
- Dominant language
- C#
- Stars
- 12.7k
- Forks
- 1.1k
- Avg merge
- 11h 1m
- Merged PRs (30d)
- 3
Description
# Change ref safety errors to warnings inside unsafe
* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
The language will downgrade `ref` errors to a warning inside an `unsafe` context.
## Motivation
[motivation]: #motivation
The set of `ref` and features in C# are concerned with providing type safe access to memory references. This includes `ref` returns, `ref` fields, `in`, etc ... The features are very successful in providing the expressiveness required by our API authors and developers. The analysis though is naturally very conservative. Essentially there are many scenarios in which `ref` could be used safely but it is flagged as an error because the conservative nature of our analysis does not understand the use case.
Today this results in a hard error being produced and the language contains no escape hatch. Instead when developers confront these cases they must rely on runtime APIs. For example [Unsafe.AsRef](https://grep.app/search?q=Unsafe.AsRef) as a way to work around these limitations.
Relying on runtime APIs has a number of significant drawbacks:
- They cannot be relied on in multi-targeting scenarios. When a new API in `net7.0` to support a C# 11 feature that cannot be used when multi-targeting between `net7.0` and say `net6.0`.
- They require non-trivial JIT work. The APIs cannot be implemented in C# code. It must be implemented as a codegen / JIT trick and done so such that it's a wash. The API intent is to really have no impact on the final code, it's an annotation.
- APIs cannot easily express some aspects of the language. Consider for example that [Unsafe.AsRef](
https://grep.app/search?q=Unsafe.AsRef) does not work for a `ref struct` and designing an API that does is virtually impossible due to lack of generic support.
- It hides the actual problems. These APIs effectively turn off language analysis so there
- It's ugly to read long streams of `Unsafe.AsRef` calls
The language already has a mechanism to allow for type and memory safety violations: `unsafe` context. It is already possible to violate memory safety using `unsafe` that would allow for exactly the same type of problems that `ref` safety means to prevent. When the language is viewed in its totality it's inconsistent that we allow effectively unlimited memory safety violations with pointers (where we have no tracking of what you can do) but don't allow for a much more constrained violation (when compiler can help identify the exact points and reasons for unsafety).
Downgrading `ref` safety errors to warnings allows for an escape hatch for developers. It still requires three levels of acknowledgement of the safety issues: `/unsafe` compiler switch, `unsafe` context and suppression of the emitted diagnostic. It also means curious developers can quickly see what violations exist by removing the warning suppressions.
## Detailed design
[design]: #detailed-design
The language will identify the errors which fall into the `ref` memory safety category. For each which "break glass in case of emergency" is required the compilre will provide a warning to pair with the error. In the case the diagnostic occurs in `unsafe` the warning will be used instead of the error.
Initially the error codes will include:
- Address of
- `ref` lifetime violations
Example of the impact this will have:
```csharp
static void M1(ref A a)
{
B b = CreateB(stackalloc byte[42]);
M2(ref a, ref b);
}
static Span M2(ref A a, ref B b)
{
// b never assigns through a so this is safe, we just can't represent it
}
ref struct A { }
ref struct B{ }
```
## Drawbacks
[drawbacks]: #drawbacks
It increase the power of `unsafe`
## Alternatives
[alternatives]: #alternatives
The alternative is we continue to lean on the runtime team to provide APIs which act as safety hatches for `unsafe`. This means more APIs like `Unsafe.AsRef`. This functions but has several downsides including cost to JIT team, cost to API design and that it doesn't work in multi-targeted scenarios.
## Unresolved questions
[unresolved]: #unresolved-questions
The full set of errors that should fall into this category. There are several we know are in the initial set. The team would be open to more errors falling into this category given compelling scenarios existing.
The other question is whether `readonly` violations should fall under the same umbrella. Generally `readonly` is there for memory safety reasons, ensuring a piece of memory is not written to. Downgrading that to a warning would seemingly fit into the same category.
## Design meetings
* https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-21.md#unsafer-unsafeness
## Related
- https://github.com/dotnet/roslyn/issues/63104
- https://github.com/dotnet/roslyn/pull/64064
- https://github.com/dotnet/csharplang/pull/6453
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.