Skip to content

Commit a2dd664

Browse files
committed
[Rust] Fixes bug with async return! behavior
1 parent 107814e commit a2dd664

3 files changed

Lines changed: 56 additions & 0 deletions

File tree

src/Fable.Transforms/Rust/Replacements.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3023,6 +3023,9 @@ let asyncBuilder (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp
30233023
| "Return", _, _ ->
30243024
Helper.LibCall(com, "AsyncBuilder", "r_return", t, args, i.SignatureArgTypes, ?loc = r)
30253025
|> Some
3026+
| "ReturnFrom", _, _ ->
3027+
Helper.LibCall(com, "AsyncBuilder", "return_from", t, args, i.SignatureArgTypes, ?loc = r)
3028+
|> Some
30263029
| "Zero", _, _ ->
30273030
Helper.LibCall(com, "AsyncBuilder", "zero", t, args, i.SignatureArgTypes, ?loc = r)
30283031
|> Some

src/fable-library-rust/src/Async.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ pub mod AsyncBuilder_ {
129129
})
130130
}
131131

132+
pub fn return_from<T: Send + Sync + 'static>(computation: Arc<Async<T>>) -> Arc<Async<T>> {
133+
computation
134+
}
135+
132136
pub fn zero<T: Send + Sync + 'static>() -> Arc<Async<()>> {
133137
r_return(())
134138
}

tests/Rust/tests/src/AsyncTests.fs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,55 @@ let shouldConvertTaskToASyncAndEvalCorrectly () =
3333
let t = task { return 1 } |> Async.AwaitTask
3434
t |> Async.RunSynchronously |> equal 1
3535

36+
[<Fact>]
37+
let ``return! should compile in async CE`` () =
38+
let inner () = async { return 42 }
39+
let outer () = async { return! inner () }
40+
let result = Async.RunSynchronously (outer ())
41+
result |> equal 42
42+
43+
[<Fact>]
44+
let ``return! works in recursive async CE`` () =
45+
let rec loop n = async {
46+
if n <= 0 then return 0
47+
else return! loop (n - 1)
48+
}
49+
let result = Async.RunSynchronously (loop 5)
50+
result |> equal 0
51+
52+
[<Fact>]
53+
let ``return! is transparent through multiple layers`` () =
54+
// Verifies return_from is identity: chaining return! does not double-wrap the computation
55+
// or alter the value in any way.
56+
let inner () = async { return 7 }
57+
let passthrough (comp: Async<int>) = async { return! comp }
58+
let result =
59+
inner ()
60+
|> passthrough
61+
|> passthrough
62+
|> passthrough
63+
|> Async.RunSynchronously
64+
result |> equal 7
65+
66+
[<Fact>]
67+
let ``return! propagates value from async built with bind`` () =
68+
// Verifies that a computation produced via let! / return is correctly
69+
// passed through return!, not just a literal return value.
70+
let doubled x = async {
71+
let! v = async { return x }
72+
return v * 2
73+
}
74+
let outer () = async { return! doubled 21 }
75+
Async.RunSynchronously (outer ()) |> equal 42
76+
77+
[<Fact>]
78+
let ``return! propagates error Result from inner async`` () =
79+
// Both Ok and Error variants must flow through return! unchanged.
80+
let inner (result: Result<int, string>) = async { return result }
81+
let outer (result: Result<int, string>) = async { return! inner result }
82+
Async.RunSynchronously (outer (Ok 99)) |> equal (Ok 99)
83+
Async.RunSynchronously (outer (Error "oops")) |> equal (Error "oops")
84+
3685
// [<Fact>]
3786
// let shouldExecAsParallelStructurallyCorrect () =
3887
// let t = Async.Parallel [

0 commit comments

Comments
 (0)