Request for debug team - always stepping in to a call
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 22h
- Merged PRs (30d)
- 144
Description
I've been looking at debugging issues for computation expressions as part of #13339 . It's ultimately a request for the .NET debugger team but I'll write it up here first. cc @gregg-miskelly who the .NET team suggested as a contact in the Visual Studio debugger.
I believe we should look into a new attribute [DebuggerStepIntoAttribute ](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggerstepthroughattribute?view=net-6.0) (or something similar) to partner DebuggerStepThroughAttribute, where putting the new attribute on a method has the effect of changing a F10 "Step Over" into a "Step Into".
This would have use in debugging for F# computation expressions, for expressing the "calling the continuation that gives the rest of the computation" part of a bind operation. It would also have many other uses in F# code, where "the rest of the method" is sometimes compiled as a separate method (this is an optimization F# sometimes performs for large methods).
We actually also need this to somehow apply to an indirect call. For example, the typical situation is that
```fsharp
let f() =
cancellable {
print "hello"
let! res1 = SomeOtherCancellable() <--- F10 Step Over here
print "world"
}
```
From the user's point of view the sequence points are:
```fsharp
let f() =
cancellable {
// Sequence Point 1
print "hello"
// Sequence Point 2 <--- F10 Step Over here
let! res1 = SomeOtherCancellable()
// Sequence Point 3
print "world"
}
```
And "Step Over" should go through these three sequence points. However the code gets compiled as follows - each chunk becomes a function that gets called, and these are then stitched together.
```fsharp
let f() =
cancellable.Delay(fun () ->
print "hello"
cancellable.Bind( SomeOtherCancellable(), fun() ->
print "world"
)
)
```
After inlining and some flattening of `cancellable.Bind` becomes
```fsharp
let f() =
Cancellable(fun ct ->
// Sequence Point 1
print "hello"
// Sequence Point 2
let computation1 = SomeOtherCancellable()
let res1 = computation1.Invoke(ct)
let computation2 =
Cancellable(fun ct ->
// Sequence Point 3
print "world")
computation2.Invoke(ct) <--- need to step into here to reach Sequence Point 3
)
```
The effect we want is that a Step Over at Sequence Point 2 would step over al the invocations and then actially Step **Into** `computation2.Invoke(ct)`. Exactly how we achieve that I'm not sure. Somehow we need to mark that call as "always step into".
Contributor guide
Assessment
This issue has not been assessed yet.