From 31019c85a487d3997ee6be3d83cce29ce333cfd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=BB=D0=B0=D0=B2=D0=B0=20=D0=A3=D0=BA=D1=80=D0=B0?= =?UTF-8?q?=D1=97=D0=BD=D1=96!=20=D0=93=D0=B5=D1=80=D0=BE=D1=8F=D0=BC=20?= =?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=D0=B0!?= <777696+ncave@users.noreply.github.com> Date: Mon, 21 Oct 2024 05:16:44 +0000 Subject: [PATCH] [Rust] Fixed try finally handler (#3933) --- src/Fable.Cli/CHANGELOG.md | 4 + src/Fable.Transforms/FableTransforms.fs | 2 +- src/Fable.Transforms/Rust/Fable2Rust.fs | 17 ++-- src/fable-library-rust/src/Exception.rs | 11 +-- tests/Rust/tests/src/TailCallTests.fs | 100 ++++++++++++------------ tests/Rust/tests/src/TypeTests.fs | 2 +- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index c663aaf999..c2f0b3a450 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Rust] Support more System.Array methods and tests (by @ncave) * [JS] Add `System.String.Normalize` support (by @DashieTM) +### Fixed + +* [Rust] Fixed try finally handler order of execution (by @ncave) + ## 4.22.0 - 2024-10-02 ### Added diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 228b0bbf6f..832e93f0a7 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -35,7 +35,7 @@ let isTailRecursive identName expr = getSubExpressions e |> List.iter (loop false) | Sequential exprs -> let lastIndex = (List.length exprs) - 1 - exprs |> List.iteri (fun i e -> loop (i = lastIndex) e) + exprs |> List.iteri (fun i e -> loop (inTailPos && i = lastIndex) e) | Let(_, value, body) -> loop false value loop inTailPos body diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index d6f497c691..c0d3d6bbf5 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -2829,10 +2829,12 @@ module Util = // try...finally match finalizer with | Some finBody -> - let f = makeLocalLambda com ctx [] finBody - let finAlloc = makeLibCall com ctx None "Exception" "finally" [ f ] + let f = transformLambda com ctx None [] finBody + let finCall = makeLibCall com ctx None "Exception" "finally" [ f ] + let finPat = makeFullNameIdentPat "__finally__" + let letExpr = mkLetExpr finPat finCall let bodyExpr = transformExpr com ctx body - [ finAlloc |> mkSemiStmt; bodyExpr |> mkExprStmt ] |> mkStmtBlockExpr + [ letExpr |> mkSemiStmt; bodyExpr |> mkExprStmt ] |> mkStmtBlockExpr | _ -> // no catch, no finalizer transformExpr com ctx body @@ -3635,11 +3637,10 @@ module Util = TailCallOpportunity = tco } - let isTailRecursive (name: string option) (body: Fable.Expr) = - if name.IsNone then - false, false - else - FableTransforms.isTailRecursive name.Value body + let isTailRecursive (nameOpt: string option) (body: Fable.Expr) = + match nameOpt with + | Some name -> FableTransforms.isTailRecursive name body + | None -> false, false let transformFunctionBody com ctx (args: Fable.Ident list) (body: Fable.Expr) = match ctx.TailCallOpportunity with diff --git a/src/fable-library-rust/src/Exception.rs b/src/fable-library-rust/src/Exception.rs index 59de3d6f0e..c744dd17f1 100644 --- a/src/fable-library-rust/src/Exception.rs +++ b/src/fable-library-rust/src/Exception.rs @@ -1,5 +1,5 @@ pub mod Exception_ { - use crate::Native_::{Any, Box_, LrcPtr}; + use crate::Native_::{Any, Box_, Func0, LrcPtr}; use crate::String_::{fromSlice, string}; use crate::System::Exception; use crate::Util_::new_Exception; @@ -41,14 +41,9 @@ pub mod Exception_ { } } - pub struct finally(pub F) - where - F: FnMut() -> R; + pub struct finally(pub Func0); - impl Drop for finally - where - F: FnMut() -> R, - { + impl Drop for finally { fn drop(&mut self) { (self.0)(); } diff --git a/tests/Rust/tests/src/TailCallTests.fs b/tests/Rust/tests/src/TailCallTests.fs index 65ca1d54ac..8facc62f8a 100644 --- a/tests/Rust/tests/src/TailCallTests.fs +++ b/tests/Rust/tests/src/TailCallTests.fs @@ -73,28 +73,28 @@ module Functions = | 0 -> x | _ -> iterate f (n - 1) (f x) - // let recWithFinally () = - // let mutable log = "" - // let rec test n = - // try - // log <- log + string "abcde"[n] - // if n < 4 then test (n+1) - // finally - // log <- log + string "ABCDE"[n] - // test 0 - // log - - // let recWithUse () = - // let mutable log = "" - // let disp(n) = - // { new System.IDisposable with - // member x.Dispose() = log <- log + string "ABCDE"[n] } - // let rec test n = - // use _disp = disp(n) - // log <- log + string "abcde"[n] - // if n < 4 then test (n+1) else 0 - // test 0 |> ignore - // log + let recWithFinally () = + let mutable log = "" + let rec test n = + try + log <- log + string "abcde".[n] + if n < 4 then test (n+1) + finally + log <- log + string "ABCDE".[n] + test 0 + log + + let recWithUse () = + let mutable log = "" + let disp(n) = + { new System.IDisposable with + member x.Dispose() = log <- log + string "ABCDE".[n] } + let rec test n = + use _disp = disp(n) + log <- log + string "abcde".[n] + if n < 4 then test (n+1) else 0 + test 0 |> ignore + log open Functions @@ -133,13 +133,13 @@ and parseTokens tokens = function | x::xs -> parseTokens tokens xs | [] -> List.rev tokens -// type Element = -// | Element of action: (unit->unit) * children: Element list -// member this.Activate() = -// match this with -// | Element(action, children) -> -// action() -// for child in children do child.Activate() +type Element = + | Element of action: (unit->unit) * children: Element list + member this.Activate() = + match this with + | Element(action, children) -> + action() + for child in children do child.Activate() [] let ``Tailcall works in tail position`` () = @@ -204,13 +204,13 @@ let ``Tailcall optimization doesn't cause endless loops`` () = // See #675 |> tryFind "a" |> equal None -// [] -// let ``Recursive functions containing finally work`` () = -// recWithFinally () |> equal "abcdeEDCBA" +[] +let ``Recursive functions containing finally work`` () = + recWithFinally () |> equal "abcdeEDCBA" -// [] -// let ``Recursive functions containing use work`` () = -// recWithUse () |> equal "abcdeEDCBA" +[] +let ``Recursive functions containing use work`` () = + recWithUse () |> equal "abcdeEDCBA" [] let ``Function arguments can be optimized`` () = // See #681 @@ -220,22 +220,22 @@ let ``Function arguments can be optimized`` () = // See #681 let ``Function arguments can be optimized II`` () = // See #681 iterate ((*) 2) 5 10 |> equal 320 -// // See https://github.com/fable-compiler/Fable/issues/1368#issuecomment-434142713 -// [] -// let ``State of internally mutated tail called function parameters is preserved properly`` () = -// let rec loop i lst = -// if i <= 0 -// then lst -// else loop (i - 1) ((fun () -> i) :: lst) -// loop 3 [] |> List.map (fun f -> f()) |> equal [1;2;3] +// See https://github.com/fable-compiler/Fable/issues/1368#issuecomment-434142713 +[] +let ``State of internally mutated tail called function parameters is preserved properly`` () = + let rec loop i lst = + if i <= 0 + then lst + else loop (i - 1) ((fun () -> i) :: lst) + loop 3 [] |> List.map (fun f -> f()) |> equal [1;2;3] -// [] -// let ``State of internally mutated tail called function parameters is preserved properly II`` () = -// let rec loop lst i = -// if i <= 0 -// then lst -// else loop ((fun () -> i) :: lst) (i - 1) -// loop [] 3 |> List.map (fun f -> f()) |> equal [1;2;3] +[] +let ``State of internally mutated tail called function parameters is preserved properly II`` () = + let rec loop lst i = + if i <= 0 + then lst + else loop ((fun () -> i) :: lst) (i - 1) + loop [] 3 |> List.map (fun f -> f()) |> equal [1;2;3] // See https://github.com/fable-compiler/Fable/issues/1368#issuecomment-434142713 [] diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index 12c5f4ca0f..f469e02f96 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -1132,7 +1132,7 @@ let ``copying struct records works`` () = // See #3371 // let ``reraise works`` () = // try // try -// Exception("Will I be reraised?") |> raise +// System.Exception("Will I be reraised?") |> raise // with _ -> // try // reraise()