From bcc31e6a3285567bd7064a21c573f1c51bd3a93b Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 3 Feb 2016 19:21:28 +1100 Subject: [PATCH 1/3] Removing boxing on equality of System.Enum objects Use magical System.Collections.Generic.EqualityComparer.Default for typeof<_>.IsEnum --- src/fsharp/FSharp.Core/prim-types.fs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index bf1168b597d..6ff1b7e7145 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -1793,6 +1793,16 @@ namespace Microsoft.FSharp.Core if x.HasValue then x.Value.GetHashCode () else 0 + module EqualityComparerDefault = + [] + type Impl<'a> = + interface IEssenceOfEquals<'a> with + member __.Ensorcel (_:IEqualityComparer, x:'a, y:'a) = + System.Collections.Generic.EqualityComparer.Default.Equals (x, y) + interface IEssenceOfGetHashCode<'a> with + member __.Ensorcel (_:IEqualityComparer, x:'a) = + System.Collections.Generic.EqualityComparer.Default.GetHashCode x + module ValueType = [] type StructuralEquatable<'a when 'a : struct and 'a :> IStructuralEquatable> = @@ -2628,6 +2638,11 @@ namespace Microsoft.FSharp.Core elif t.Equals typeof then typeof else null + let enumTypes (t:Type) : Type = + if t.IsEnum + then mos.makeType t typedefof> + else null + let compilerGenerated tyRelation ty = // if we are using the ER comparer, and we are a standard f# record or value type with compiler generated // equality operators, then we can avoid the boxing of IStructuralEquatable and just call the @@ -2699,6 +2714,7 @@ namespace Microsoft.FSharp.Core fun () -> tuples tyRelation ty fun () -> floatingPointTypes tyRelation ty fun () -> standardTypes ty + fun () -> enumTypes ty fun () -> compilerGenerated tyRelation ty fun () -> arrays tyRelation ty fun () -> nullableType ty @@ -3052,6 +3068,11 @@ namespace Microsoft.FSharp.Core elif t.Equals typeof then typeof else null + let enumTypes (t:Type) : Type = + if t.IsEnum + then mos.makeType t typedefof> + else null + [] type GenericHashParamObject<'a> = interface IEssenceOfGetHashCode<'a> with @@ -3093,6 +3114,7 @@ namespace Microsoft.FSharp.Core mos.takeFirstNonNull [| fun () -> tuples t fun () -> standardTypes t + fun () -> enumTypes t fun () -> arrays t fun () -> nullableType t fun () -> structualEquatable t From b37f855280419b61ce0999f365585da8cbd34690 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 3 Feb 2016 19:48:37 +1100 Subject: [PATCH 2/3] Tests for equality of System.Enum derived objects Tests try to ensure that the compiler doesn't inline the functionality --- .../FSharp.Core/PrimTypes.fs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/fsharp/FSharp.Core.Unittests/FSharp.Core/PrimTypes.fs b/src/fsharp/FSharp.Core.Unittests/FSharp.Core/PrimTypes.fs index 8f23db3a41e..a1dde2fbde8 100644 --- a/src/fsharp/FSharp.Core.Unittests/FSharp.Core/PrimTypes.fs +++ b/src/fsharp/FSharp.Core.Unittests/FSharp.Core/PrimTypes.fs @@ -683,3 +683,55 @@ type UnboxAndOptionStuff() = Assert.IsTrue( not (isNull [| |])) Assert.IsTrue( not (isNull "")) Assert.IsTrue( not (isNull "1")) + + +type EnumA = +| A0 = 0 +| A1 = 1 +| A2 = 2 + +module EnumEqualityTestHelper = + let equals<'a when 'a : equality> (lhs:'a) (rhs:'a) = + // we are needlessly complex here to avoid the compiler from inlining this function + let r = System.Random () + let n = r.Next () + let mutable result = Unchecked.defaultof<_> + for i = n to n do + result <- lhs = rhs + result + + let equals2<'a when 'a : equality> (lhs:'a) (rhs:'a) = + LanguagePrimitives.FastGenericEqualityComparer.Equals (lhs, rhs) + +[] +type EnumEqualityTest() = + [] + member this.EqualityCheck () = + Assert.IsTrue (EnumEqualityTestHelper.equals EnumA.A0 EnumA.A0) + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A0 EnumA.A1) + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A0 EnumA.A2) + + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A1 EnumA.A0) + Assert.IsTrue (EnumEqualityTestHelper.equals EnumA.A1 EnumA.A1) + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A1 EnumA.A2) + + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A2 EnumA.A0) + Assert.IsFalse (EnumEqualityTestHelper.equals EnumA.A2 EnumA.A1) + Assert.IsTrue (EnumEqualityTestHelper.equals EnumA.A2 EnumA.A2) + + [] + member this.EqualityCheck2 () = + Assert.IsTrue (EnumEqualityTestHelper.equals2 EnumA.A0 EnumA.A0) + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A0 EnumA.A1) + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A0 EnumA.A2) + + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A1 EnumA.A0) + Assert.IsTrue (EnumEqualityTestHelper.equals2 EnumA.A1 EnumA.A1) + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A1 EnumA.A2) + + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A2 EnumA.A0) + Assert.IsFalse (EnumEqualityTestHelper.equals2 EnumA.A2 EnumA.A1) + Assert.IsTrue (EnumEqualityTestHelper.equals2 EnumA.A2 EnumA.A2) + + + From 13a1129bf67c6846bb2d346bcf8b1ef6d4203ad0 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 3 Feb 2016 20:36:25 +1100 Subject: [PATCH 3/3] Add IsEnum for reshaped reflection To support alternative profiles --- src/fsharp/FSharp.Core/prim-types.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 6ff1b7e7145..7086c4dadd7 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -402,6 +402,7 @@ namespace Microsoft.FSharp.Core type System.Type with member inline this.IsGenericType = this.GetTypeInfo().IsGenericType member inline this.IsValueType = this.GetTypeInfo().IsValueType + member inline this.IsEnum = this.GetTypeInfo().IsEnum member inline this.IsSealed = this.GetTypeInfo().IsSealed member inline this.IsAssignableFrom(otherTy : Type) = this.GetTypeInfo().IsAssignableFrom(otherTy.GetTypeInfo()) member inline this.GetGenericArguments() = this.GetTypeInfo().GenericTypeArguments