Skip to content

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

@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
    No fields configured for issues without a type.

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions