feat(Async): Add exception-unwrapping Await - #19785
Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
Warning No PR link found in some release notes, please consider adding it.
|
b2b2b6a to
b710cc1
Compare
|
@T-Gro if you and/or others can give this a quick scan please, I'd like to confirm:
TL;DR the overall proposition
CC @TheAngryByrd @gusty @njlr who have provided useful feedback/review on stuff in this space in recent times |
There was a problem hiding this comment.
Pull request overview
Adds a new Async.Await API to FSharp.Core for Task/Task<'T> (and ValueTask/ValueTask<'T> on netstandard2.1) that unwraps “egregious” single-inner AggregateExceptions so exception matching works as expected, while aiming to preserve existing AwaitTask stacktrace behavior.
Changes:
- Implement
Async.Awaitinasync.fsby sharing the existingAwaitTaskcontinuation machinery and selectively unwrapping single-innerAggregateExceptions. - Expand unit tests to exercise both
AwaitTaskandAwaitbehavior (including AggregateException cases) and add ValueTask coverage under#if NETSTANDARD2_1. - Update FSharp.Core surface area baseline (partially) and add a release-notes entry.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs | Converts many tests to run against both AwaitTask and new Await; adds new behavior-focused tests for AggregateException unwrapping and ValueTask. |
| tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl | Adds Async.Await entries for netstandard2.1 Debug surface area baseline. |
| src/FSharp.Core/async.fsi | Updates AwaitTask XML docs and adds new Async.Await API docs (including ValueTask overloads under netstandard2.1). |
| src/FSharp.Core/async.fs | Implements Async.Await and refactors task-await internals to optionally unwrap single-inner AggregateExceptions. |
| docs/release-notes/.FSharp.Core/11.0.100.md | Adds release note entry for new Async.Await. |
7aec92d to
9ab202c
Compare
|
I'm thinking about naming. |
@majocha Yes, this was already part of the brief as per the comment from @dsyme. I'd personally need to research what it would entail, so if you or anyone wants to contribute an impl, feel free to hang a PR off this one. But bottom line I agree it would be good for the support for tasklike things to be done either as part of this PR or as a very fast follow so the world only needs to validate the overloading works out cleanly once. |
a887f40 to
885c40f
Compare
. @majocha Added a commit:
|
93badc9 to
50814f5
Compare
Discussed with Tomas in dotnet#19785 (comment)
This comment has been minimized.
This comment has been minimized.
|
@T-Gro updated to target your branch and merged fsharp/main from earlier today Also @wallymathieu @gusty there's another new compiler interaction with F#+ source:
|
|
@bartelink Yes, I need to change those |
|
@T-Gro Update:- your PR got merged into FSharpPlus; I've repointed this at the post-merge commit on master
|
The function-domain unification order change in PR dotnet#15181 (nullness) caused recursive inline SRTP resolution to be truncated by one currying level: an inference variable still carrying an unsolved member constraint could be absorbed by the required domain, merging away the pending recursive trait resolution. This regressed patterns such as FSharpPlus `memoizeN`. SolveFunTypeEqn now keeps the SRTP-constrained inference variable as the unification representative for that specific case, and only when not MatchingOnly (mirroring SolveTypeEqualsType). Adds two ComponentTests (the memoizeN repro across currying depths, and a MatchingOnly overload-resolution guard) and repoints the FSharpPlus regression matrix at a minimal global.json-bump branch that exercises the fix end-to-end. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4cfb1c86-0d7e-45e6-9529-3c5f301c0912
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4cfb1c86-0d7e-45e6-9529-3c5f301c0912
|
Superseded by #20247 @T-Gro confirming the remaining blocker is the fact that the FSharpPlus regression build has a a failing test in its memoization module. The project itself is green at the same commit hash using an 8.0 sdk. Thus analysis is required to determine whether the memoization was working by coincidence in that context, or whether the updated compiler/toolset is to blame/broken. I see there is/was a .NET 10 branch that commented out the assertion as the same issue was observed - the simplest 🙈 workaround would be to make a PR against FSharpPlus with that one liner commenting out of the assertion and then pin to that. If you have any ideas on a path to resolving the issue, I may be able to pitch in, but for now it's surrounded by a large SEP field my side :P |
a6ed74b to
2317986
Compare
# Conflicts: # azure-pipelines-PR.yml
…sion' into async-await # Conflicts: # azure-pipelines-PR.yml
|
🔍 Tooling Safety Check — Affects-Build-Infra, Affects-Compiler-Output
|
|
@T-Gro Nice work resolving the This is green now (with #20427 merged into here temporarily) and all FSharpPlus regressions pointed at your f42f81885111c652b08218e0880c264447ae56e4 temp commit in FSharpPlus After this merges, I intend to proceed with bartelink#1 |
|
Congrats for pushing trough all the obstacles encountered on the road 🥇 . The F#+ pin should now remain to this SHA. And once the compiler change lands, we can also open the same PR to F#+/master. |
Implements
Async.AwaitforTask,Task<'T>,ValueTaskandValueTask<'T>(aka a polished version of thecommunity
AwaitTaskCorrectimplementation). Includes a fallback SRTP based type augmentation that uses theGetAwaiterprotocol to support custom waits a la C#await.Key differentiation from
Async.AwaitTaskis thatAggregateExceptions are unwrapped such that atry ... with <ExceptionType> ->will type-match correctly.Key distinction from the canonical implementation (which derives from https://www.fssnip.net/7Rc/title/AsyncAwaitTaskCorrect) is that the implementation is intended to have 1:1 matching of all stacktrace preservation properties borne by
AwaitTask(and continue to track that over time).NOTE one key implementation decision is that this PR does NOT attempt to resolve #2127 so:
Awaitis invoked, normal cancellation semantics as perAwaitTaskapply:OperationCanceledExceptionTaskin question'sResultwill go unobservedAwait, it will (likeAwaitTask):Taskawaitwould] until such time as theTaskcompletes (either successfully, with a fault, or via cancellation)Updates the FSharpPlus regression test commit reference to include fsprojects/FSharpPlus#677, which aligns the semantics with those here and in the canonical fssnip base implementation.
Checklist
AwaitTaskwill always yield anAggregateExceptionAwaitshould be the used in preference for new codeAwaitcan technically still propagate anAggregateException