From a2dd66404b9395ca7c856907da94e5cfb2cafe02 Mon Sep 17 00:00:00 2001 From: Mike <6625993+mizzle-mo@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:23:07 -0600 Subject: [PATCH] [Rust] Fixes bug with async return! behavior --- src/Fable.Transforms/Rust/Replacements.fs | 3 ++ src/fable-library-rust/src/Async.rs | 4 ++ tests/Rust/tests/src/AsyncTests.fs | 49 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/Fable.Transforms/Rust/Replacements.fs b/src/Fable.Transforms/Rust/Replacements.fs index 7a7b3c8244..861aeb0763 100644 --- a/src/Fable.Transforms/Rust/Replacements.fs +++ b/src/Fable.Transforms/Rust/Replacements.fs @@ -3023,6 +3023,9 @@ let asyncBuilder (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp | "Return", _, _ -> Helper.LibCall(com, "AsyncBuilder", "r_return", t, args, i.SignatureArgTypes, ?loc = r) |> Some + | "ReturnFrom", _, _ -> + Helper.LibCall(com, "AsyncBuilder", "return_from", t, args, i.SignatureArgTypes, ?loc = r) + |> Some | "Zero", _, _ -> Helper.LibCall(com, "AsyncBuilder", "zero", t, args, i.SignatureArgTypes, ?loc = r) |> Some diff --git a/src/fable-library-rust/src/Async.rs b/src/fable-library-rust/src/Async.rs index c9e4b9f0b9..b04f3eab4d 100644 --- a/src/fable-library-rust/src/Async.rs +++ b/src/fable-library-rust/src/Async.rs @@ -129,6 +129,10 @@ pub mod AsyncBuilder_ { }) } + pub fn return_from(computation: Arc>) -> Arc> { + computation + } + pub fn zero() -> Arc> { r_return(()) } diff --git a/tests/Rust/tests/src/AsyncTests.fs b/tests/Rust/tests/src/AsyncTests.fs index 4c00c4f7ea..2e69a91ea8 100644 --- a/tests/Rust/tests/src/AsyncTests.fs +++ b/tests/Rust/tests/src/AsyncTests.fs @@ -33,6 +33,55 @@ let shouldConvertTaskToASyncAndEvalCorrectly () = let t = task { return 1 } |> Async.AwaitTask t |> Async.RunSynchronously |> equal 1 +[] +let ``return! should compile in async CE`` () = + let inner () = async { return 42 } + let outer () = async { return! inner () } + let result = Async.RunSynchronously (outer ()) + result |> equal 42 + +[] +let ``return! works in recursive async CE`` () = + let rec loop n = async { + if n <= 0 then return 0 + else return! loop (n - 1) + } + let result = Async.RunSynchronously (loop 5) + result |> equal 0 + +[] +let ``return! is transparent through multiple layers`` () = + // Verifies return_from is identity: chaining return! does not double-wrap the computation + // or alter the value in any way. + let inner () = async { return 7 } + let passthrough (comp: Async) = async { return! comp } + let result = + inner () + |> passthrough + |> passthrough + |> passthrough + |> Async.RunSynchronously + result |> equal 7 + +[] +let ``return! propagates value from async built with bind`` () = + // Verifies that a computation produced via let! / return is correctly + // passed through return!, not just a literal return value. + let doubled x = async { + let! v = async { return x } + return v * 2 + } + let outer () = async { return! doubled 21 } + Async.RunSynchronously (outer ()) |> equal 42 + +[] +let ``return! propagates error Result from inner async`` () = + // Both Ok and Error variants must flow through return! unchanged. + let inner (result: Result) = async { return result } + let outer (result: Result) = async { return! inner result } + Async.RunSynchronously (outer (Ok 99)) |> equal (Ok 99) + Async.RunSynchronously (outer (Error "oops")) |> equal (Error "oops") + // [] // let shouldExecAsParallelStructurallyCorrect () = // let t = Async.Parallel [