`assert` keyword: have compiler emit call to `Debug.Assert` overload with `CallerArgumentExpression` where possible
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 22h
- Merged PRs (30d)
- 144
Description
Following https://github.com/dotnet/fsharp/pull/17519, we should see if it is possible for the F# compiler to emit a call to the `System.Diagnostics.Debug.Assert` overload that takes a message defaulting to `CallerArgumentExpression` for all usages of the `assert` keyword.
I.e., an expression like this
```fsharp
assert not true
```
would be translated to
```fsharp
System.Diagnostics.Debug.Assert (not true, "not true")
```
Likewise:
```fsharp
assert (x = 3)
```
→
```fsharp
System.Diagnostics.Debug.Assert ((x = 3), "(x = 3)")
```
etc.
The C# compiler now does this for `System.Diagnostics.Debug.Assert(booleanExpr)` by means of the `OverloadResolutionPriorityAttribute`, which F# does not currently support: https://github.com/dotnet/fsharp/issues/16967#issuecomment-2218267688
> > [dotnet/csharplang#7906](https://github.com/dotnet/csharplang/pull/7906): developers can add weight to which methods are better in overload resolution. This seems unlikely to impact F# as much.
>
> Want to point out one place this will intersect with F#. Consider that very likely `Debug` will end up looking like the following:
>
> ```csharp
> public static class Debug
> {
> [OverloadResolutionPriority(-1)]
> public static void Assert(bool condition) { ... }
>
> public static void Assert(bool condition, [CallerArgumentExpression] string? message = "") { ... }
> }
> ```
> Nothing will break for F# here when this happens, code will still compile as it used to. The experience for C# though will improve from a lot of `Debug.Assert failed` messages to the actual expression that passed into the assert. This is one part I thought might be interesting to F# .
See the [BCL source](https://github.com/dotnet/runtime/blob/4144fdab16a98dc7bf8509e6850eb691ddede804/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs#L80-L87):
```csharp
[Conditional("DEBUG")]
[OverloadResolutionPriority(-1)] // lower priority than (bool, string) overload so that the compiler prefers using CallerArgumentExpression
public static void Assert([DoesNotReturnIf(false)] bool condition) =>
Assert(condition, string.Empty, string.Empty);
[Conditional("DEBUG")]
public static void Assert([DoesNotReturnIf(false)] bool condition, [CallerArgumentExpression(nameof(condition))] string? message = null) =>
Assert(condition, message, string.Empty);
```
[SharpLab F#](https://sharplab.io/#v2:DYLgZgzgNAJiDUAfYBTALgAjBgHhgvBgIYQQoBOmAFHoQMwCUQA=)
[SharpLab C#](https://sharplab.io/#v2:CYLg1APgAgTABAYQLACgDeq5blAjANhwBY4AxACgEsA7AFzgA8BKOAXgD4dcA6AEUoCGAc2oB7AM61KAY3F8ApgCMArkO4BBcePkAnWuQZtWcAMxMA3KgC+QA===)
We could in theory do this in the F# compiler specifically for the `assert` keyword, without needing to support `OverloadResolutionPriorityAttribute` in general, by emitting a call to the new overload when it is available here:
https://github.com/dotnet/fsharp/blob/69be2cd095394e3791dd26c5dcb45bd35d098535/src/Compiler/Checking/Expressions/CheckExpressions.fs#L7699-L7706
Contributor guide
Assessment
This issue has not been assessed yet.