Skip to content

Generic Record Types have different Equality semantics to concrete types #527

@manofstick

Description

@manofstick

From #513

The problem is that for concrete types, the implementation of IStructuralEquatable optimizes standard types, which means that the IEqualityComparer is unused.

The following example shows this by using a specialized IEqualityComparer to try to do an uppercase string comparison.

type A = { A : string }
type B<'a> = { B : 'a }

[<EntryPoint>]
let main argv =
    let a1, a2 = { A = "Hello"}, { A = "HELLO" }
    let b1, b2 = { B = "Hello"}, { B = "HELLO" }

    let compareStringWithUpper=
        let toupper (s:string) = s.ToUpper ()
        { new IEqualityComparer with
            member this.GetHashCode item =
                match item with
                | :? string as s -> (toupper s).GetHashCode ()
                | _ -> failwith "Not in this example..."
            member this.Equals (lhs, rhs) =
                match lhs, rhs with
                | (:? string as s1), (:? string as s2) -> (toupper s1).Equals(toupper s2)
                | _ -> failwith "Not in this example..." }

    let a_is_good = (a1 :> IStructuralEquatable).Equals(a2, compareStringWithUpper)
    let b_is_good = (b1 :> IStructuralEquatable).Equals(b2, compareStringWithUpper)

    printfn "A is %s" <| if a_is_good then "Good" else "Bad"
    printfn "B is %s" <| if b_is_good then "Good" else "Bad"

    if a_is_good <> b_is_good then
        printfn "But worse than those results are that they are inconsistent!"

With the output

A is Bad
B is Good
But worse than those results are that they are inconsistent!

I think the implementation of IStructuralEquatable.Equals shouldn't used inlined IL, but rather always defer back to supplied IEqualityComparer.

Fixing this before #513 is implemented would have a performance impact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-Compiler-OptimizationThe F# optimizer, release code gen etc.BugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

    Type

    No fields configured for Bug.

    Projects

    Status

    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions