dotnet / dotnet/fsharp

Generic Record Types have different Equality semantics to concrete types

Open
#527 0 comments 0 reactions 0 assignees View on GitHub
Area-Compiler-Optimization Bug Impact-Low
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 11h
Merged PRs (30d)
131

Description

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 }

[]
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.