fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

[Regression] Compiler is unable to compile specific flavor of type check in patterns

Open En3Tho opened this issue 2 years ago • 2 comments

Please provide a succinct description of the issue.

I've found a case where compiler is not able to compile the code it used to compile before. This particular flavor of pattern macthing is used to help compiler figure out some overload complexities

Repro steps

Provide the steps required to reproduce the problem:

open System

module Repro =
    let fail1() =
        let x: Result<unit, exn> = Error (NullReferenceException())
        match x with
        | Error (_: exn & :? System.NullReferenceException) -> // Unexpected symbol ':?' in pattern (FS0010)
            printfn "NullRef!"
        | _ ->
            ()

    let fail2() =
        let x: Result<unit, exn> = Error (Exception())
        match x with
        | Error (_: exn & (:? System.NullReferenceException)) -> // Constraint intersection syntax may only be used with flexible types, e.g. '#IDisposable & #ISomeInterface'. (FS3572)
            printfn "NullRef"
        | _ ->
            printfn "Not a NullRef"

Repro.fail1()
Repro.fail2()

Net6/7 output: (compiles and runs)

NullRef!
Not a NullRef

Net8 fails to build the app with errors specified in the code as comments

En3Tho avatar Dec 15 '23 19:12 En3Tho

Interesting. I suppose it is because & was not valid in the type syntax before, and so only the part between : and & was being parsed as a type, i.e., exn; & … and so on were being parsed as separate patterns. Now that & is valid in the type syntax (after #15413), the parser no longer stops at & after the : and instead tries keep parsing the subsequent patterns (:? … or (:? …)) as part of the type.

Parenthesizing the typed pattern itself _ : exn & …(_ : exn) & … allows your example to compile in F# 8:

module Repro =
    let fail1() =
        let x: Result<unit, exn> = Error (NullReferenceException())
        match x with
        | Error ((_: exn) & :? System.NullReferenceException) -> // Works in F# 8 with parens around the typed pattern.
            printfn "NullRef!"
        | _ ->
            ()

    let fail2() =
        let x: Result<unit, exn> = Error (Exception())
        match x with
        | Error ((_: exn) & (:? System.NullReferenceException)) -> // Works in F# 8 with parens around the typed pattern.
            printfn "NullRef"
        | _ ->
            printfn "Not a NullRef"

Repro.fail1()
Repro.fail2()

But you are right about this being a regression.

brianrourkeboll avatar Dec 15 '23 19:12 brianrourkeboll

@brianrourkeboll Thank you for finding a workaround

En3Tho avatar Dec 15 '23 19:12 En3Tho