Skip to content

assert keyword: have compiler emit call to Debug.Assert overload with CallerArgumentExpression where possible #18489

Open
@brianrourkeboll

Description

@brianrourkeboll

Following #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

assert not true

would be translated to

System.Diagnostics.Debug.Assert (not true, "not true")

Likewise:

assert (x = 3)

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: #16967 (comment)

dotnet/csharplang#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:

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:

[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#
SharpLab C#

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:

// Check an 'assert x' expression.
and TcAssertExpr cenv overallTy env (m: range) tpenv x =
let synm = m.MakeSynthetic() // Mark as synthetic so the language service won't pick it up.
let callDiagnosticsExpr = SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet synm ["System";"Diagnostics";"Debug"] "Assert",
// wrap an extra parentheses so 'assert(x=1) isn't considered a named argument to a method call
SynExpr.Paren (x, range0, None, synm), synm)
TcExpr cenv overallTy env tpenv callDiagnosticsExpr

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions