Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Fable.Transforms/Python/Fable2Python.Transforms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3007,6 +3007,16 @@ let transformFunction
let cleanName (input: string) =
Regex.Replace(input, @"_mut(_\d+)?$", "")

// Names of this function's own arguments. TCO capture parameters must not
// collide with these, otherwise we emit a duplicate argument (see #4610).
let ownArgNames =
args
|> List.map (fun id ->
let (Identifier name) = ident com ctx id
name
)
|> Set.ofList

// For Python we need to append the TC-arguments to any declared (arrow) function inside the while-loop of the
// TCO. We will set them as default values to themselves e.g `i=i` to capture the value and not the variable.
let tcArgs, tcDefaults =
Expand All @@ -3019,6 +3029,11 @@ let transformFunction

match name with
| "tupled_arg_m" -> None // Remove these arguments (not sure why)
// Don't capture a TCO variable as a default parameter when this
// function already declares an argument with the same name: the
// local argument shadows the outer one and a duplicate parameter
// is a Python syntax error. See #4610.
| _ when ownArgNames.Contains name -> None
// Only capture TCO variables actually referenced in the function body.
// This avoids unnecessary default parameters on nested lambdas that don't
// use the outer TCO variables. See #3877.
Expand Down
16 changes: 16 additions & 0 deletions tests/Python/TestPatternMatch.fs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,22 @@ let ``test guard expression capture with 5 cases`` () =
guardCaptureMultiple 5 |> equal "small: 5"
guardCaptureMultiple 0 |> equal "zero or negative"

// Guard that uses the bound value multiple times. The guard is hoisted into a
// helper function and must not capture the outer argument as a duplicate
// default parameter (see #4610).
let mkTag (tag: string option) =
match tag with
| None -> ""
| Some s when s.StartsWith("!") && not (s.StartsWith("!!")) -> s + " "
| Some s -> "!<" + s + "> "

[<Fact>]
let ``test guard reusing binding does not duplicate captured argument`` () =
mkTag None |> equal ""
mkTag (Some "!foo") |> equal "!foo "
mkTag (Some "!!foo") |> equal "!<!!foo> "
mkTag (Some "foo") |> equal "!<foo> "

// ----------------------------------------------------------------------------
// 6. Nested Matching
// ----------------------------------------------------------------------------
Expand Down
Loading