Skip to content

Match expression is evaluated twice if it returns a struct union #5689

Description

@cmeeren

Potentially critical issue: If the expression (e.g. a function call) inside a match ... with returns a struct union (e.g. ValueOption<_>, Result<_,_> or a custom struct union), the expression is evaluated twice, including any side effects.

Repro steps

Repro solution: Test.zip ConsoleApp1.zip

The repro solution consists of a console app with the following code, showing various examples and aspects of this bug:

open System


type CustomValueUnion = V

[<Struct>]
type CustomStructUnion = S

[<Struct>]
type CustomStructRecord =
  { A: CustomStructUnion }


[<EntryPoint>]
let main argv =

  match printfn "Option inlined"; Some () with
  | Some () -> ()
  | None -> ()

  let x = printfn "Option outside"; Some ()
  match x with
  | Some () -> ()
  | None -> ()

  match printfn "ValueOption inlined"; ValueSome () with
  | ValueSome () -> ()
  | ValueNone -> ()

  let x = printfn "ValueOption outside"; ValueSome ()
  match x with
  | ValueSome () -> ()
  | ValueNone -> ()

  match printfn "Result inlined"; Ok () with
  | Ok () -> ()
  | Error () -> ()

  let x = printfn "Result outside"; Ok ()
  match x with
  | Ok () -> ()
  | Error () -> ()

  match printfn "Custom value union inlined"; V with
  | V -> ()

  let x = printfn "Custom value union outside"; V
  match x with
  | V -> ()

  match printfn "Custom struct union inlined"; S with
  | S -> ()

  let x = printfn "Custom struct union outside"; S
  match x with
  | S -> ()

  match printfn "Custom struct union inlined with catch-all"; S with
  | _ -> ()

  match printfn "Custom struct record inlined"; { A = S } with
  | { A = S } -> ()


  printfn "Press Enter to exit..."
  Console.ReadLine() |> ignore
  0

Expected behavior

The message should only be printed once for each use of match.

Actual behavior

image

Known workarounds

Bind the expression to a named value and use that value in the match expression, like the example shows.

Related information

  • Visual Studio version: 15.8.5
  • Visual F# tools 10.2 for F# 4.5, 15.8.0.0, commit hash 92247b8
  • Severity: Assumed high

Metadata

Metadata

Assignees

No one assigned

    Labels

    Impact-High(Internal MS Team use only) Describes an issue with extreme impact on existing code.Regression

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions