diff --git a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
index 489b9188a2b..972578c7f3e 100644
--- a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
+++ b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
@@ -111,11 +111,8 @@
Utilities/EditDistance.fs
-
- Utilities/TaggedCollections.fsi
-
-
- Utilities/TaggedCollections.fs
+
+ Utilities/SortKey.fs
Utilities/QueueList.fs
@@ -135,18 +132,6 @@
Utilities/filename.fs
-
- Utilities/zmap.fsi
-
-
- Utilities/zmap.fs
-
-
- Utilities/zset.fsi
-
-
- Utilities/zset.fs
-
Utilities/bytes.fsi
diff --git a/src/absil/ilreflect.fs b/src/absil/ilreflect.fs
index c07408e8ab6..7a913101428 100644
--- a/src/absil/ilreflect.fs
+++ b/src/absil/ilreflect.fs
@@ -27,8 +27,6 @@ open Microsoft.FSharp.Core.Printf
open Microsoft.FSharp.Core.ReflectionAdapters
#endif
-let codeLabelOrder = ComparisonIdentity.Structural
-
// Convert the output of convCustomAttr
open Microsoft.FSharp.Compiler.AbstractIL.ILAsciiWriter
let wrapCustomAttr setCustomAttr (cinfo, bytes) =
@@ -284,8 +282,8 @@ type System.Reflection.Emit.ILGenerator with
let inline flagsIf b x = if b then x else enum 0
-module Zmap =
- let force x m str = match Zmap.tryFind x m with Some y -> y | None -> failwithf "Zmap.force: %s: x = %+A" str x
+module Map =
+ let force x m str = match Map.tryFind x m with Some y -> y | None -> failwithf "Map.force: %s: x = %+A" str x
let equalTypes (s:Type) (t:Type) = s.Equals(t)
let equalTypeLists ss tt = List.lengthsEqAndForall2 equalTypes ss tt
@@ -363,30 +361,25 @@ let convTypeRefAux (cenv:cenv) (tref:ILTypeRef) =
/// and could be placed as hash tables in the global environment.
[]
type emEnv =
- { emTypMap : Zmap ;
- emConsMap : Zmap;
- emMethMap : Zmap;
- emFieldMap : Zmap;
- emPropMap : Zmap;
+ { emTypMap : Map
+ emConsMap : Map;
+ emMethMap : Map
+ emFieldMap : Map
+ emPropMap : Map
emLocals : LocalBuilder[];
- emLabels : Zmap;
+ emLabels : Map
emTyvars : Type[] list; // stack
emEntryPts : (TypeBuilder * string) list
delayedFieldInits : (unit -> unit) list}
-let orderILTypeRef = ComparisonIdentity.Structural
-let orderILMethodRef = ComparisonIdentity.Structural
-let orderILFieldRef = ComparisonIdentity.Structural
-let orderILPropertyRef = ComparisonIdentity.Structural
-
let emEnv0 =
- { emTypMap = Zmap.empty orderILTypeRef;
- emConsMap = Zmap.empty orderILMethodRef;
- emMethMap = Zmap.empty orderILMethodRef;
- emFieldMap = Zmap.empty orderILFieldRef;
- emPropMap = Zmap.empty orderILPropertyRef;
+ { emTypMap = Map.empty
+ emConsMap = Map.empty
+ emMethMap = Map.empty
+ emFieldMap = Map.empty
+ emPropMap = Map.empty
emLocals = [| |];
- emLabels = Zmap.empty codeLabelOrder;
+ emLabels = Map.empty
emTyvars = [];
emEntryPts = []
delayedFieldInits = [] }
@@ -394,13 +387,13 @@ let emEnv0 =
let envBindTypeRef emEnv (tref:ILTypeRef) (typT, typB, typeDef)=
match typT with
| null -> failwithf "binding null type in envBindTypeRef: %s\n" tref.Name;
- | _ -> {emEnv with emTypMap = Zmap.add tref (typT, typB, typeDef, None) emEnv.emTypMap}
+ | _ -> {emEnv with emTypMap = Map.add tref (typT, typB, typeDef, None) emEnv.emTypMap}
let envUpdateCreatedTypeRef emEnv (tref:ILTypeRef) =
// The tref's TypeBuilder has been created, so we have a Type proper.
// Update the tables to include this created type (the typT held prior to this is (i think) actually (TypeBuilder :> Type).
// The (TypeBuilder :> Type) does not implement all the methods that a Type proper does.
- let typT, typB, typeDef, _createdTypOpt = Zmap.force tref emEnv.emTypMap "envGetTypeDef: failed"
+ let typT, typB, typeDef, _createdTypOpt = Map.force tref emEnv.emTypMap "envGetTypeDef: failed"
if typB.IsCreated() then
let ty = typB.CreateTypeAndLog()
#if ENABLE_MONO_SUPPORT
@@ -415,7 +408,7 @@ let envUpdateCreatedTypeRef emEnv (tref:ILTypeRef) =
System.Runtime.Serialization.FormatterServices.GetUninitializedObject(ty) |> ignore
with e -> ()
#endif
- {emEnv with emTypMap = Zmap.add tref (typT, typB, typeDef, Some ty) emEnv.emTypMap}
+ {emEnv with emTypMap = Map.add tref (typT, typB, typeDef, Some ty) emEnv.emTypMap}
else
#if DEBUG
printf "envUpdateCreatedTypeRef: expected type to be created\n";
@@ -424,7 +417,7 @@ let envUpdateCreatedTypeRef emEnv (tref:ILTypeRef) =
let convTypeRef cenv emEnv preferCreated (tref:ILTypeRef) =
let res =
- match Zmap.tryFind tref emEnv.emTypMap with
+ match Map.tryFind tref emEnv.emTypMap with
| Some (_typT, _typB, _typeDef, Some createdTy) when preferCreated -> createdTy
| Some (typT, _typB, _typeDef, _) -> typT
| None -> convTypeRefAux cenv tref
@@ -433,35 +426,35 @@ let convTypeRef cenv emEnv preferCreated (tref:ILTypeRef) =
| _ -> res
let envBindConsRef emEnv (mref:ILMethodRef) consB =
- {emEnv with emConsMap = Zmap.add mref consB emEnv.emConsMap}
+ {emEnv with emConsMap = Map.add mref consB emEnv.emConsMap}
let envGetConsB emEnv (mref:ILMethodRef) =
- Zmap.force mref emEnv.emConsMap "envGetConsB: failed"
+ Map.force mref emEnv.emConsMap "envGetConsB: failed"
let envBindMethodRef emEnv (mref:ILMethodRef) methB =
- {emEnv with emMethMap = Zmap.add mref methB emEnv.emMethMap}
+ {emEnv with emMethMap = Map.add mref methB emEnv.emMethMap}
let envGetMethB emEnv (mref:ILMethodRef) =
- Zmap.force mref emEnv.emMethMap "envGetMethB: failed"
+ Map.force mref emEnv.emMethMap "envGetMethB: failed"
let envBindFieldRef emEnv fref fieldB =
- {emEnv with emFieldMap = Zmap.add fref fieldB emEnv.emFieldMap}
+ {emEnv with emFieldMap = Map.add fref fieldB emEnv.emFieldMap}
let envGetFieldB emEnv fref =
- Zmap.force fref emEnv.emFieldMap "- envGetMethB: failed"
+ Map.force fref emEnv.emFieldMap "- envGetMethB: failed"
let envBindPropRef emEnv (pref:ILPropertyRef) propB =
- {emEnv with emPropMap = Zmap.add pref propB emEnv.emPropMap}
+ {emEnv with emPropMap = Map.add pref propB emEnv.emPropMap}
let envGetPropB emEnv pref =
- Zmap.force pref emEnv.emPropMap "- envGetPropB: failed"
+ Map.force pref emEnv.emPropMap "- envGetPropB: failed"
let envGetTypB emEnv (tref:ILTypeRef) =
- Zmap.force tref emEnv.emTypMap "envGetTypB: failed"
+ Map.force tref emEnv.emTypMap "envGetTypB: failed"
|> (fun (_typT, typB, _typeDef, _createdTypOpt) -> typB)
let envGetTypeDef emEnv (tref:ILTypeRef) =
- Zmap.force tref emEnv.emTypMap "envGetTypeDef: failed"
+ Map.force tref emEnv.emTypMap "envGetTypeDef: failed"
|> (fun (_typT, _typB, typeDef, _createdTypOpt) -> typeDef)
let envSetLocals emEnv locs = assert (emEnv.emLocals.Length = 0); // check "locals" is not yet set (scopes once only)
@@ -469,11 +462,11 @@ let envSetLocals emEnv locs = assert (emEnv.emLocals.Length = 0); // check "loca
let envGetLocal emEnv i = emEnv.emLocals.[i] // implicit bounds checking
let envSetLabel emEnv name lab =
- assert (not (Zmap.mem name emEnv.emLabels));
- {emEnv with emLabels = Zmap.add name lab emEnv.emLabels}
+ assert (not (Map.containsKey name emEnv.emLabels));
+ {emEnv with emLabels = Map.add name lab emEnv.emLabels}
let envGetLabel emEnv name =
- Zmap.find name emEnv.emLabels
+ Map.find name emEnv.emLabels
let envPushTyvars emEnv tys = {emEnv with emTyvars = tys :: emEnv.emTyvars}
let envPopTyvars emEnv = {emEnv with emTyvars = List.tail emEnv.emTyvars}
@@ -487,7 +480,7 @@ let envGetTyvar emEnv u16 =
else
tvs.[i]
-let isEmittedTypeRef emEnv tref = Zmap.mem tref emEnv.emTypMap
+let isEmittedTypeRef emEnv tref = Map.containsKey tref emEnv.emTypMap
let envAddEntryPt emEnv mref = {emEnv with emEntryPts = mref::emEnv.emEntryPts}
let envPopEntryPts emEnv = {emEnv with emEntryPts = []}, emEnv.emEntryPts
@@ -2147,6 +2140,6 @@ let LookupTypeRef cenv emEnv tref = convCreatedTypeRef cenv emEnv tref
let LookupType cenv emEnv ty = convCreatedType cenv emEnv ty
// Lookups of ILFieldRef and MethodRef may require a similar non-Builder-fixup post Type-creation.
-let LookupFieldRef emEnv fref = Zmap.tryFind fref emEnv.emFieldMap |> Option.map (fun fieldBuilder -> fieldBuilder :> FieldInfo)
-let LookupMethodRef emEnv mref = Zmap.tryFind mref emEnv.emMethMap |> Option.map (fun methodBuilder -> methodBuilder :> MethodInfo)
+let LookupFieldRef emEnv fref = Map.tryFind fref emEnv.emFieldMap |> Option.map (fun fieldBuilder -> fieldBuilder :> FieldInfo)
+let LookupMethodRef emEnv mref = Map.tryFind mref emEnv.emMethMap |> Option.map (fun methodBuilder -> methodBuilder :> MethodInfo)
diff --git a/src/absil/zmap.fs b/src/absil/zmap.fs
deleted file mode 100644
index c08767e2744..00000000000
--- a/src/absil/zmap.fs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
-
-namespace Microsoft.FSharp.Compiler.AbstractIL.Internal
-
-open Internal.Utilities.Collections.Tagged
-open System.Collections.Generic
-
-/// Maps with a specific comparison function
-type internal Zmap<'Key,'T> = Internal.Utilities.Collections.Tagged.Map<'Key,'T>
-
-[]
-module internal Zmap =
-
- let empty (ord: IComparer<'T>) = Map<_,_,_>.Empty(ord)
-
- let add k v (m:Zmap<_,_>) = m.Add(k,v)
- let find k (m:Zmap<_,_>) = m.[k]
- let tryFind k (m:Zmap<_,_>) = m.TryFind(k)
- let remove k (m:Zmap<_,_>) = m.Remove(k)
- let mem k (m:Zmap<_,_>) = m.ContainsKey(k)
- let iter f (m:Zmap<_,_>) = m.Iterate(f)
- let first f (m:Zmap<_,_>) = m.First(fun k v -> if f k v then Some (k,v) else None)
- let exists f (m:Zmap<_,_>) = m.Exists(f)
- let forall f (m:Zmap<_,_>) = m.ForAll(f)
- let map f (m:Zmap<_,_>) = m.MapRange(f)
- let mapi f (m:Zmap<_,_>) = m.Map(f)
- let fold f (m:Zmap<_,_>) x = m.Fold f x
- let toList (m:Zmap<_,_>) = m.ToList()
- let foldSection lo hi f (m:Zmap<_,_>) x = m.FoldSection lo hi f x
-
- let isEmpty (m:Zmap<_,_>) = m.IsEmpty
-
- let foldMap f z (m:Zmap<_,_>) =
- let m,z = m.FoldAndMap (fun k v z -> let z,v' = f z k v in v',z) z in
- z,m
-
- let choose f (m:Zmap<_,_>) = m.First(f)
-
- let chooseL f (m:Zmap<_,_>) =
- m.Fold (fun k v s -> match f k v with None -> s | Some x -> x::s) []
-
- let ofList ord xs = Internal.Utilities.Collections.Tagged.Map<_,_>.FromList(ord,xs)
-
- let keys (m:Zmap<_,_>) = m.Fold (fun k _ s -> k::s) []
- let values (m:Zmap<_,_>) = m.Fold (fun _ v s -> v::s) []
-
- let memberOf m k = mem k m
diff --git a/src/absil/zmap.fsi b/src/absil/zmap.fsi
deleted file mode 100644
index 12a17400029..00000000000
--- a/src/absil/zmap.fsi
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
-
-namespace Microsoft.FSharp.Compiler.AbstractIL.Internal
-
-open Internal.Utilities
-open Internal.Utilities.Collections.Tagged
-open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
-open System.Collections.Generic
-
-/// Maps with a specific comparison function
-type internal Zmap<'Key,'T> = Internal.Utilities.Collections.Tagged.Map<'Key,'T>
-
-[]
-module internal Zmap =
-
- val empty : IComparer<'Key> -> Zmap<'Key,'T>
- val isEmpty : Zmap<'Key,'T> -> bool
-
- val add : 'Key -> 'T -> Zmap<'Key,'T> -> Zmap<'Key,'T>
- val remove : 'Key -> Zmap<'Key,'T> -> Zmap<'Key,'T>
- val mem : 'Key -> Zmap<'Key,'T> -> bool
- val memberOf : Zmap<'Key,'T> -> 'Key -> bool
- val tryFind : 'Key -> Zmap<'Key,'T> -> 'T option
- val find : 'Key -> Zmap<'Key,'T> -> 'T // raises KeyNotFoundException
-
- val map : mapping:('T -> 'U) -> Zmap<'Key,'T> -> Zmap<'Key,'U>
- val mapi : ('Key -> 'T -> 'U) -> Zmap<'Key,'T> -> Zmap<'Key,'U>
- val fold : ('Key -> 'T -> 'U -> 'U) -> Zmap<'Key,'T> -> 'U -> 'U
- val foldMap : ('State -> 'Key -> 'T -> 'State * 'U) -> 'State -> Zmap<'Key,'T> -> 'State * Zmap<'Key,'U>
- val iter : action:('T -> 'U -> unit) -> Zmap<'T, 'U> -> unit
-
- val foldSection: 'Key -> 'Key -> ('Key -> 'T -> 'U -> 'U) -> Zmap<'Key,'T> -> 'U -> 'U
-
- val first : ('Key -> 'T -> bool) -> Zmap<'Key,'T> -> ('Key * 'T) option
- val exists : ('Key -> 'T -> bool) -> Zmap<'Key,'T> -> bool
- val forall : ('Key -> 'T -> bool) -> Zmap<'Key,'T> -> bool
-
- val choose : ('Key -> 'T -> 'U option) -> Zmap<'Key,'T> -> 'U option
- val chooseL : ('Key -> 'T -> 'U option) -> Zmap<'Key,'T> -> 'U list
-
- val toList : Zmap<'Key,'T> -> ('Key * 'T) list
- val ofList : IComparer<'Key> -> ('Key * 'T) list -> Zmap<'Key,'T>
-
- val keys : Zmap<'Key,'T> -> 'Key list
- val values : Zmap<'Key,'T> -> 'T list
diff --git a/src/absil/zset.fs b/src/absil/zset.fs
deleted file mode 100644
index 960caa06e36..00000000000
--- a/src/absil/zset.fs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
-
-namespace Microsoft.FSharp.Compiler.AbstractIL.Internal
-
-open Microsoft.FSharp.Compiler.AbstractIL
-open Microsoft.FSharp.Compiler.AbstractIL.Internal
-open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
-open Internal.Utilities
-open Internal.Utilities.Collections.Tagged
-open System.Collections.Generic
-
-/// Sets with a specific comparison function
-type internal Zset<'T> = Internal.Utilities.Collections.Tagged.Set<'T>
-
-[]
-module internal Zset =
-
- let empty (ord : IComparer<'T>) = Internal.Utilities.Collections.Tagged.Set<_,_>.Empty(ord)
-
- let isEmpty (s:Zset<_>) = s.IsEmpty
-
- let contains x (s:Zset<_>) = s.Contains(x)
- let add x (s:Zset<_>) = s.Add(x)
- let addList xs a = List.fold (fun a x -> add x a) a xs
-
- let singleton ord x = add x (empty ord)
- let remove x (s:Zset<_>) = s.Remove(x)
-
- let fold (f : 'T -> 'b -> 'b) (s:Zset<_>) b = s.Fold f b
- let iter f (s:Zset<_>) = s.Iterate f
- let forall p (s:Zset<_>) = s.ForAll p
- let count (s:Zset<_>) = s.Count
- let exists p (s:Zset<_>) = s.Exists p
- let subset (s1:Zset<_>) (s2:Zset<_>) = s1.IsSubsetOf s2
- let equal (s1:Zset<_>) (s2:Zset<_>) = Internal.Utilities.Collections.Tagged.Set<_,_>.Equality(s1,s2)
- let elements (s:Zset<_>) = s.ToList()
- let filter p (s:Zset<_>) = s.Filter p
-
- let union (s1:Zset<_>) (s2:Zset<_>) = Internal.Utilities.Collections.Tagged.Set<_,_>.Union(s1,s2)
- let inter (s1:Zset<_>) (s2:Zset<_>) = Internal.Utilities.Collections.Tagged.Set<_,_>.Intersection(s1,s2)
- let diff (s1:Zset<_>) (s2:Zset<_>) = Internal.Utilities.Collections.Tagged.Set<_,_>.Difference(s1,s2)
-
- let memberOf m k = contains k m
diff --git a/src/absil/zset.fsi b/src/absil/zset.fsi
deleted file mode 100644
index 094e0288128..00000000000
--- a/src/absil/zset.fsi
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
-
-namespace Microsoft.FSharp.Compiler.AbstractIL.Internal
-
-open Internal.Utilities
-open Microsoft.FSharp.Compiler.AbstractIL
-open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
-open System.Collections.Generic
-
-/// Sets with a specific comparison function
-type internal Zset<'T> = Internal.Utilities.Collections.Tagged.Set<'T>
-
-
-[]
-module internal Zset =
-
- val empty : IComparer<'T> -> Zset<'T>
- val isEmpty : Zset<'T> -> bool
- val contains : 'T -> Zset<'T> -> bool
- val memberOf : Zset<'T> -> 'T -> bool
- val add : 'T -> Zset<'T> -> Zset<'T>
- val addList : 'T list -> Zset<'T> -> Zset<'T>
- val singleton : IComparer<'T> -> 'T -> Zset<'T>
- val remove : 'T -> Zset<'T> -> Zset<'T>
-
- val count : Zset<'T> -> int
- val union : Zset<'T> -> Zset<'T> -> Zset<'T>
- val inter : Zset<'T> -> Zset<'T> -> Zset<'T>
- val diff : Zset<'T> -> Zset<'T> -> Zset<'T>
- val equal : Zset<'T> -> Zset<'T> -> bool
- val subset : Zset<'T> -> Zset<'T> -> bool
- val forall : predicate:('T -> bool) -> Zset<'T> -> bool
- val exists : predicate:('T -> bool) -> Zset<'T> -> bool
- val filter : predicate:('T -> bool) -> Zset<'T> -> Zset<'T>
-
- val fold : ('T -> 'State -> 'State) -> Zset<'T> -> 'State -> 'State
- val iter : ('T -> unit) -> Zset<'T> -> unit
-
- val elements : Zset<'T> -> 'T list
-
-
-
diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
index 1668c425ebe..23836872cba 100644
--- a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
+++ b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
@@ -75,11 +75,8 @@
Utilities\EditDistance.fs
-
- Utilities\TaggedCollections.fsi
-
-
- Utilities\TaggedCollections.fs
+
+ Utilities\SortKey.fs
Utilities\ildiag.fsi
@@ -96,18 +93,6 @@
Utilities\filename.fs
-
- Utilities\zmap.fsi
-
-
- Utilities\zmap.fs
-
-
- Utilities\zset.fsi
-
-
- Utilities\zset.fs
-
Utilities\bytes.fsi
diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs
index d54373c5548..f0fe182cdec 100644
--- a/src/fsharp/CompileOps.fs
+++ b/src/fsharp/CompileOps.fs
@@ -5329,11 +5329,16 @@ let CheckSimulateException(tcConfig:TcConfig) =
// Type-check sets of files
//--------------------------------------------------------------------------
-type RootSigs = Zmap
-type RootImpls = Zset
-
let qnameOrder = Order.orderBy (fun (q:QualifiedNameOfFile) -> q.Text)
+[]
+type QNameOrder =
+ interface System.Collections.Generic.IComparer with
+ member __.Compare(v1, v2) = qnameOrder.Compare (v1,v2)
+
+type RootSigs = zmap
+type RootImpls = zset
+
type TcState =
{ tcsCcu: CcuThunk
tcsCcuType: ModuleOrNamespace
@@ -5403,8 +5408,8 @@ let GetInitialTcState(m, ccuName, tcConfig:TcConfig, tcGlobals, tcImports:TcImpo
tcsTcSigEnv=tcEnv0
tcsTcImplEnv=tcEnv0
tcsCreatesGeneratedProvidedTypes=false
- tcsRootSigs = Zmap.empty qnameOrder
- tcsRootImpls = Zset.empty qnameOrder
+ tcsRootSigs = Zmap.empty ()
+ tcsRootImpls = Zset.empty ()
tcsCcuSig = NewEmptyModuleOrNamespaceType Namespace }
@@ -5425,7 +5430,7 @@ let TypeCheckOneInputEventually (checkForErrors, tcConfig:TcConfig, tcImports:Tc
| ParsedInput.SigFile (ParsedSigFileInput(_, qualNameOfFile, _, _, _) as file) ->
// Check if we've seen this top module signature before.
- if Zmap.mem qualNameOfFile tcState.tcsRootSigs then
+ if Zmap.containsKey qualNameOfFile tcState.tcsRootSigs then
errorR(Error(FSComp.SR.buildSignatureAlreadySpecified(qualNameOfFile.Text), m.StartRange))
// Check if the implementation came first in compilation order
@@ -5461,7 +5466,7 @@ let TypeCheckOneInputEventually (checkForErrors, tcConfig:TcConfig, tcImports:Tc
| ParsedInput.ImplFile (ParsedImplFileInput(_, _, qualNameOfFile, _, _, _, _) as file) ->
// Check if we've got an interface for this fragment
- let rootSigOpt = tcState.tcsRootSigs.TryFind(qualNameOfFile)
+ let rootSigOpt = tcState.tcsRootSigs.TryFind {CompareObj=qualNameOfFile}
// Check if we've already seen an implementation for this fragment
if Zset.contains qualNameOfFile tcState.tcsRootImpls then
diff --git a/src/fsharp/DetupleArgs.fs b/src/fsharp/DetupleArgs.fs
index bf56deeeb7f..40dab48cb94 100644
--- a/src/fsharp/DetupleArgs.fs
+++ b/src/fsharp/DetupleArgs.fs
@@ -2,6 +2,8 @@
module internal Microsoft.FSharp.Compiler.Detuple
+open Internal.Utilities.Collections
+
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
@@ -163,7 +165,7 @@ module GlobalUsageAnalysis =
let GetValsBoundInExpr expr =
let folder = {ExprFolder0 with valBindingSiteIntercept = bindAccBounds}
- let z0 = Zset.empty valOrder
+ let z0 = Zset.empty ()
let z = FoldExpr folder z0 expr
z
@@ -181,22 +183,22 @@ module GlobalUsageAnalysis =
/// (b) log it's binding site representation.
type Results =
{ /// v -> context / APP inst args
- Uses : Zmap
+ Uses : zmap
/// v -> binding repr
- Defns : Zmap
+ Defns : zmap
/// bound in a decision tree?
- DecisionTreeBindings : Zset
+ DecisionTreeBindings : zset
/// v -> v list * recursive? -- the others in the mutual binding
- RecursiveBindings : Zmap
- TopLevelBindings : Zset
+ RecursiveBindings : zmap
+ TopLevelBindings : zset
IterationIsAtTopLevel : bool }
let z0 =
- { Uses = Zmap.empty valOrder
- Defns = Zmap.empty valOrder
- RecursiveBindings = Zmap.empty valOrder
- DecisionTreeBindings = Zset.empty valOrder
- TopLevelBindings = Zset.empty valOrder
+ { Uses = Zmap.empty ()
+ Defns = Zmap.empty ()
+ RecursiveBindings = Zmap.empty ()
+ DecisionTreeBindings = Zset.empty ()
+ TopLevelBindings = Zset.empty ()
IterationIsAtTopLevel = true }
/// Log the use of a value with a particular tuple chape at a callsite
@@ -611,7 +613,7 @@ let determineTransforms g (z : GlobalUsageAnalysis.Results) =
decideTransform g z f callPatterns (m, tps, vss, rty) // make transform (if required)
let vtransforms = Zmap.chooseL selectTransform z.Uses
- let vtransforms = Zmap.ofList valOrder vtransforms
+ let vtransforms = Zmap.ofList vtransforms
vtransforms
@@ -622,7 +624,7 @@ let determineTransforms g (z : GlobalUsageAnalysis.Results) =
type penv =
{ // The planned transforms
- transforms : Zmap
+ transforms : zmap
ccu : CcuThunk
g : TcGlobals }
diff --git a/src/fsharp/DetupleArgs.fsi b/src/fsharp/DetupleArgs.fsi
index 58412315820..babe5e20ba1 100644
--- a/src/fsharp/DetupleArgs.fsi
+++ b/src/fsharp/DetupleArgs.fsi
@@ -2,6 +2,7 @@
module internal Microsoft.FSharp.Compiler.Detuple
+open Internal.Utilities.Collections
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.TcGlobals
@@ -9,7 +10,7 @@ open Microsoft.FSharp.Compiler.TcGlobals
val DetupleImplFile : CcuThunk -> TcGlobals -> TypedImplFile -> TypedImplFile
module GlobalUsageAnalysis =
- val GetValsBoundInExpr : Expr -> Zset
+ val GetValsBoundInExpr : Expr -> zset
type accessor
@@ -18,15 +19,15 @@ module GlobalUsageAnalysis =
/// Later could support "safe" change operations, and optimisations could be in terms of those.
type Results =
{ /// v -> context / APP inst args
- Uses : Zmap;
+ Uses : zmap
/// v -> binding repr
- Defns : Zmap;
+ Defns : zmap
/// bound in a decision tree?
- DecisionTreeBindings : Zset;
+ DecisionTreeBindings : zset
/// v -> recursive? * v list -- the others in the mutual binding
- RecursiveBindings : Zmap;
+ RecursiveBindings : zmap
/// val not defined under lambdas
- TopLevelBindings : Zset;
+ TopLevelBindings : zset
/// top of expr toplevel? (true)
IterationIsAtTopLevel : bool;
}
diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
index 4b5affe091c..b2bb687f25c 100644
--- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
+++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj
@@ -113,11 +113,8 @@
Utilities\EditDistance.fs
-
- Utilities\TaggedCollections.fsi
-
-
- Utilities\TaggedCollections.fs
+
+ Utilities\SortKey.fs
Utilities\ildiag.fsi
@@ -134,18 +131,6 @@
Utilities\filename.fs
-
- Utilities\zmap.fsi
-
-
- Utilities\zmap.fs
-
-
- Utilities\zset.fsi
-
-
- Utilities\zset.fs
-
Utilities\bytes.fsi
diff --git a/src/fsharp/FSharp.Core/local.fs b/src/fsharp/FSharp.Core/local.fs
index 42297795184..343b0409c26 100644
--- a/src/fsharp/FSharp.Core/local.fs
+++ b/src/fsharp/FSharp.Core/local.fs
@@ -990,8 +990,12 @@ module internal Array =
open System
- let inline fastComparerForArraySort<'t when 't : comparison> () =
- LanguagePrimitives.FastGenericComparerCanBeNull<'t>
+ let inline getInternalComparer<'t when 't : comparison> () =
+ // Previously a "comparer" was returned the could be null, which was for optimized Array.Sort
+ // but we now mainly return Comparer.Default (and FastGenericComparerInternal more so)
+ // which is also optimized in Array.Sort
+ // ** this comment can be destroyed sometime in the future, it is just as a breadcrumb for review **
+ LanguagePrimitives.FastGenericComparerInternal<'t>
// The input parameter should be checked by callers if necessary
let inline zeroCreateUnchecked (count:int) =
@@ -1089,26 +1093,26 @@ module internal Array =
let keys = zeroCreateUnchecked array.Length
for i = 0 to array.Length - 1 do
keys.[i] <- projection array.[i]
- Array.Sort<_,_>(keys, array, fastComparerForArraySort())
+ Array.Sort<_,_>(keys, array, getInternalComparer())
let unstableSortInPlace (array : array<'T>) =
let len = array.Length
if len < 2 then ()
- else Array.Sort<_>(array, fastComparerForArraySort())
+ else Array.Sort<_>(array, getInternalComparer())
- let stableSortWithKeysAndComparer (cFast:IComparer<'Key>) (c:IComparer<'Key>) (array:array<'T>) (keys:array<'Key>) =
+ let stableSortWithKeysAndComparer (c:IComparer<'Key>) (array:array<'T>) (keys:array<'Key>) =
// 'places' is an array or integers storing the permutation performed by the sort
let places = zeroCreateUnchecked array.Length
for i = 0 to array.Length - 1 do
places.[i] <- i
- System.Array.Sort<_,_>(keys, places, cFast)
+ System.Array.Sort<_,_>(keys, places, c)
// 'array2' is a copy of the original values
let array2 = (array.Clone() :?> array<'T>)
// Walk through any chunks where the keys are equal
let mutable i = 0
let len = array.Length
- let intCompare = fastComparerForArraySort()
+ let intCompare = getInternalComparer()
while i < len do
let mutable j = i
@@ -1123,9 +1127,8 @@ module internal Array =
i <- j
let stableSortWithKeys (array:array<'T>) (keys:array<'Key>) =
- let cFast = fastComparerForArraySort()
- let c = LanguagePrimitives.FastGenericComparer<'Key>
- stableSortWithKeysAndComparer cFast c array keys
+ let c = getInternalComparer()
+ stableSortWithKeysAndComparer c array keys
let stableSortInPlaceBy (projection: 'T -> 'U) (array : array<'T>) =
let len = array.Length
@@ -1141,13 +1144,11 @@ module internal Array =
let len = array.Length
if len < 2 then ()
else
- let cFast = LanguagePrimitives.FastGenericComparerCanBeNull<'T>
- match cFast with
- | null ->
+ if LanguagePrimitives.EquivalentForStableAndUnstableSort<'T> then
// An optimization for the cases where the keys and values coincide and do not have identity, e.g. are integers
// In this case an unstable sort is just as good as a stable sort (and faster)
Array.Sort<_,_>(array, null)
- | _ ->
+ else
// 'keys' is an array storing the projected keys
let keys = (array.Clone() :?> array<'T>)
stableSortWithKeys array keys
@@ -1158,7 +1159,7 @@ module internal Array =
let keys = (array.Clone() :?> array<'T>)
let comparer = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(comparer)
let c = { new IComparer<'T> with member __.Compare(x,y) = comparer.Invoke(x,y) }
- stableSortWithKeysAndComparer c c array keys
+ stableSortWithKeysAndComparer c array keys
let inline subUnchecked startIndex count (array : 'T[]) =
let res = zeroCreateUnchecked count : 'T[]
diff --git a/src/fsharp/FSharp.Core/local.fsi b/src/fsharp/FSharp.Core/local.fsi
index 3181dcbc6ec..3106c1bbc35 100644
--- a/src/fsharp/FSharp.Core/local.fsi
+++ b/src/fsharp/FSharp.Core/local.fsi
@@ -101,3 +101,5 @@ module internal Array =
val stableSortInPlaceWith: comparer:('T -> 'T -> int) -> array:'T[] -> unit
val stableSortInPlace: array:'T[] -> unit when 'T : comparison
+
+ val stableSortWithKeysAndComparer : System.Collections.Generic.IComparer<'Key> -> array<'T> -> array<'Key> -> unit when 'Key : comparison
\ No newline at end of file
diff --git a/src/fsharp/FSharp.Core/map.fs b/src/fsharp/FSharp.Core/map.fs
index 97c747e4ae5..fc9be5783db 100644
--- a/src/fsharp/FSharp.Core/map.fs
+++ b/src/fsharp/FSharp.Core/map.fs
@@ -8,27 +8,12 @@ namespace Microsoft.FSharp.Collections
open Microsoft.FSharp.Core
open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
- []
[]
type MapTree<'Key,'Value when 'Key : comparison > =
- | MapEmpty
- | MapOne of 'Key * 'Value
- | MapNode of 'Key * 'Value * MapTree<'Key,'Value> * MapTree<'Key,'Value> * int
- // REVIEW: performance rumour has it that the data held in MapNode and MapOne should be
- // exactly one cache line. It is currently ~7 and 4 words respectively.
+ | MapNode of Key:'Key * Value:'Value * Left:MapTree<'Key,'Value> * Right:MapTree<'Key,'Value> * Size:int
[]
module MapTree =
-
- let rec sizeAux acc m =
- match m with
- | MapEmpty -> acc
- | MapOne _ -> acc + 1
- | MapNode(_,_,l,r,_) -> sizeAux (sizeAux (acc+1) l) r
-
- let size x = sizeAux 0 x
-
-
#if TRACE_SETS_AND_MAPS
let mutable traceCount = 0
let mutable numOnes = 0
@@ -62,156 +47,135 @@ namespace Microsoft.FSharp.Collections
n
#endif
- let empty = MapEmpty
-
- let height = function
- | MapEmpty -> 0
- | MapOne _ -> 1
- | MapNode(_,_,_,_,h) -> h
-
- let isEmpty m =
- match m with
- | MapEmpty -> true
- | _ -> false
-
- let mk l k v r =
- match l,r with
- | MapEmpty,MapEmpty -> MapOne(k,v)
- | _ ->
- let hl = height l
- let hr = height r
- let m = if hl < hr then hr else hl
- MapNode(k,v,l,r,m+1)
-
- let rebalance t1 k v t2 =
- let t1h = height t1
- let t2h = height t2
- if t2h > t1h + 2 then (* right is heavier than left *)
- match t2 with
- | MapNode(t2k,t2v,t2l,t2r,_) ->
- (* one of the nodes must have height > height t1 + 1 *)
- if height t2l > t1h + 1 then (* balance left: combination *)
- match t2l with
- | MapNode(t2lk,t2lv,t2ll,t2lr,_) ->
- mk (mk t1 k v t2ll) t2lk t2lv (mk t2lr t2k t2v t2r)
- | _ -> failwith "rebalance"
- else (* rotate left *)
- mk (mk t1 k v t2l) t2k t2v t2r
- | _ -> failwith "rebalance"
+ []
+ type Constants<'Key, 'Value when 'Key : comparison> private () =
+ static let empty = MapNode(Unchecked.defaultof<'Key>, Unchecked.defaultof<'Value>, Unchecked.defaultof>, Unchecked.defaultof>, 0)
+ static member Empty = empty
+
+ let inline size (MapNode(Size=s)) = s
+ let inline key (MapNode(Key=k)) = k
+ let inline value (MapNode(Value=v)) = v
+ let inline left (MapNode(Left=l)) = l
+ let inline right (MapNode(Right=r)) = r
+
+ let inline isEmpty (MapNode(Size=s)) = s = 0
+
+ let inline (++) l r = Checked.(+) l r
+
+ let inline mk l k v r =
+ MapNode (k,v,l,r, size l ++ size r ++ 1)
+
+ let inline mkLeaf k v =
+ MapNode (k, v, Constants.Empty, Constants.Empty, 1)
+
+ let private rebalanceRight l k v (MapNode(rk,rv,rl,rr,_)) =
+ (* one of the nodes must have height > height t1 + 1 *)
+ if size rl > size l then (* balance left: combination *)
+ match rl with
+ | MapNode(rlk,rlv,rll,rlr,_) -> mk (mk l k v rll) rlk rlv (mk rlr rk rv rr)
+ else (* rotate left *)
+ mk (mk l k v rl) rk rv rr
+
+ let private rebalanceLeft (MapNode(lk,lv,ll,lr,_)) k v r =
+ (* one of the nodes must have height > height t2 + 1 *)
+ if size lr > size r then
+ (* balance right: combination *)
+ match lr with
+ | MapNode(lrk,lrv,lrl,lrr,_) -> mk (mk ll lk lv lrl) lrk lrv (mk lrr k v r)
else
- if t1h > t2h + 2 then (* left is heavier than right *)
- match t1 with
- | MapNode(t1k,t1v,t1l,t1r,_) ->
- (* one of the nodes must have height > height t2 + 1 *)
- if height t1r > t2h + 1 then
- (* balance right: combination *)
- match t1r with
- | MapNode(t1rk,t1rv,t1rl,t1rr,_) ->
- mk (mk t1l t1k t1v t1rl) t1rk t1rv (mk t1rr k v t2)
- | _ -> failwith "rebalance"
- else
- mk t1l t1k t1v (mk t1r k v t2)
- | _ -> failwith "rebalance"
- else mk t1 k v t2
-
- let rec add (comparer: IComparer<'Value>) k v m =
- match m with
- | MapEmpty -> MapOne(k,v)
- | MapOne(k2,_) ->
- let c = comparer.Compare(k,k2)
- if c < 0 then MapNode (k,v,MapEmpty,m,2)
- elif c = 0 then MapOne(k,v)
- else MapNode (k,v,m,MapEmpty,2)
- | MapNode(k2,v2,l,r,h) ->
- let c = comparer.Compare(k,k2)
- if c < 0 then rebalance (add comparer k v l) k2 v2 r
- elif c = 0 then MapNode(k,v,l,r,h)
- else rebalance l k2 v2 (add comparer k v r)
+ mk ll lk lv (mk lr k v r)
- let rec find (comparer: IComparer<'Value>) k m =
- match m with
- | MapEmpty -> raise (KeyNotFoundException())
- | MapOne(k2,v2) ->
- let c = comparer.Compare(k,k2)
- if c = 0 then v2
- else raise (KeyNotFoundException())
- | MapNode(k2,v2,l,r,_) ->
- let c = comparer.Compare(k,k2)
- if c < 0 then find comparer k l
- elif c = 0 then v2
- else find comparer k r
+ let inline rebalance l k v r =
+ let ls, rs = size l, size r
+ if (rs >>> 1) > ls then rebalanceRight l k v r
+ elif (ls >>> 1) > rs then rebalanceLeft l k v r
+ else MapNode (k,v,l,r, ls ++ rs ++ 1)
- let rec tryFind (comparer: IComparer<'Value>) k m =
- match m with
- | MapEmpty -> None
- | MapOne(k2,v2) ->
- let c = comparer.Compare(k,k2)
- if c = 0 then Some v2
- else None
- | MapNode(k2,v2,l,r,_) ->
+ let rec add (comparer:IComparer<'Key>) k v (MapNode(k2,v2,l,r,s)) =
+ if s = 0 then mkLeaf k v
+ else
let c = comparer.Compare(k,k2)
- if c < 0 then tryFind comparer k l
- elif c = 0 then Some v2
- else tryFind comparer k r
-
- let partition1 (comparer: IComparer<'Value>) (f:OptimizedClosures.FSharpFunc<_,_,_>) k v (acc1,acc2) =
+ if c < 0 then
+ let l' = add comparer k v l
+ let l's, rs = size l', size r
+ if (l's >>> 1) > rs then
+ rebalanceLeft l' k2 v2 r
+ else
+ MapNode (k2,v2,l',r, l's ++ rs ++ 1)
+ elif c > 0 then
+ let r' = add comparer k v r
+ let ls, r's = size l, size r'
+ if (r's >>> 1) > ls then
+ rebalanceRight l k2 v2 r'
+ else
+ MapNode (k2,v2,l,r', ls ++ r's ++ 1)
+ else
+ MapNode(k,v,l,r,s)
+
+ let inline private findImpl (comparer:IComparer<'Key>) k m found notfound =
+ let rec loop m =
+ if (size m) = 0 then notfound ()
+ else
+ let c = comparer.Compare(k, key m)
+ if c < 0 then loop (left m)
+ elif c > 0 then loop (right m)
+ else found (value m)
+ loop m
+
+ let find comparer k m = findImpl comparer k m id (fun () -> raise (KeyNotFoundException ()))
+ let tryFind comparer k m = findImpl comparer k m Some (fun () -> None)
+
+ let partition1 (comparer:IComparer<'Key>) (f:OptimizedClosures.FSharpFunc<_,_,_>) k v (acc1,acc2) =
if f.Invoke(k, v) then (add comparer k v acc1,acc2) else (acc1,add comparer k v acc2)
- let rec partitionAux (comparer: IComparer<'Value>) (f:OptimizedClosures.FSharpFunc<_,_,_>) s acc =
+ let rec partitionAux (comparer:IComparer<'Key>) (f:OptimizedClosures.FSharpFunc<_,_,_>) s acc =
match s with
- | MapEmpty -> acc
- | MapOne(k,v) -> partition1 comparer f k v acc
+ | MapNode(Size=0) -> acc
| MapNode(k,v,l,r,_) ->
let acc = partitionAux comparer f r acc
let acc = partition1 comparer f k v acc
partitionAux comparer f l acc
- let partition (comparer: IComparer<'Value>) f s = partitionAux comparer (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) s (empty,empty)
+ let partition (comparer:IComparer<'Key>) f s = partitionAux comparer (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) s (Constants.Empty,Constants.Empty)
- let filter1 (comparer: IComparer<'Value>) (f:OptimizedClosures.FSharpFunc<_,_,_>) k v acc = if f.Invoke(k, v) then add comparer k v acc else acc
+ let filter1 (comparer:IComparer<'Key>) (f:OptimizedClosures.FSharpFunc<_,_,_>) k v acc = if f.Invoke(k, v) then add comparer k v acc else acc
- let rec filterAux (comparer: IComparer<'Value>) (f:OptimizedClosures.FSharpFunc<_,_,_>) s acc =
+ let rec filterAux (comparer:IComparer<'Key>) (f:OptimizedClosures.FSharpFunc<_,_,_>) s acc =
match s with
- | MapEmpty -> acc
- | MapOne(k,v) -> filter1 comparer f k v acc
+ | MapNode(Size=0) -> acc
| MapNode(k,v,l,r,_) ->
let acc = filterAux comparer f l acc
let acc = filter1 comparer f k v acc
filterAux comparer f r acc
- let filter (comparer: IComparer<'Value>) f s = filterAux comparer (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) s empty
+ let filter (comparer:IComparer<'Key>) f s = filterAux comparer (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) s Constants.Empty
let rec spliceOutSuccessor m =
match m with
- | MapEmpty -> failwith "internal error: Map.spliceOutSuccessor"
- | MapOne(k2,v2) -> k2,v2,MapEmpty
+ | MapNode(Size=0) -> failwith "internal error: Map.spliceOutSuccessor"
| MapNode(k2,v2,l,r,_) ->
match l with
- | MapEmpty -> k2,v2,r
+ | MapNode(Size=0) -> k2,v2,r
| _ -> let k3,v3,l' = spliceOutSuccessor l in k3,v3,mk l' k2 v2 r
- let rec remove (comparer: IComparer<'Value>) k m =
+ let rec remove (comparer:IComparer<'Key>) k m =
match m with
- | MapEmpty -> empty
- | MapOne(k2,_) ->
- let c = comparer.Compare(k,k2)
- if c = 0 then MapEmpty else m
+ | MapNode(Size=0) -> Constants.Empty
| MapNode(k2,v2,l,r,_) ->
let c = comparer.Compare(k,k2)
if c < 0 then rebalance (remove comparer k l) k2 v2 r
elif c = 0 then
match l,r with
- | MapEmpty,_ -> r
- | _,MapEmpty -> l
+ | MapNode(Size=0),_ -> r
+ | _,MapNode(Size=0) -> l
| _ ->
let sk,sv,r' = spliceOutSuccessor r
mk l sk sv r'
else rebalance l k2 v2 (remove comparer k r)
- let rec mem (comparer: IComparer<'Value>) k m =
+ let rec mem (comparer:IComparer<'Key>) k m =
match m with
- | MapEmpty -> false
- | MapOne(k2,_) -> (comparer.Compare(k,k2) = 0)
+ | MapNode(Size=0) -> false
| MapNode(k2,_,l,r,_) ->
let c = comparer.Compare(k,k2)
if c < 0 then mem comparer k l
@@ -219,16 +183,14 @@ namespace Microsoft.FSharp.Collections
let rec iterOpt (f:OptimizedClosures.FSharpFunc<_,_,_>) m =
match m with
- | MapEmpty -> ()
- | MapOne(k2,v2) -> f.Invoke(k2, v2)
+ | MapNode(Size=0) -> ()
| MapNode(k2,v2,l,r,_) -> iterOpt f l; f.Invoke(k2, v2); iterOpt f r
let iter f m = iterOpt (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) m
let rec tryPickOpt (f:OptimizedClosures.FSharpFunc<_,_,_>) m =
match m with
- | MapEmpty -> None
- | MapOne(k2,v2) -> f.Invoke(k2, v2)
+ | MapNode(Size=0) -> None
| MapNode(k2,v2,l,r,_) ->
match tryPickOpt f l with
| Some _ as res -> res
@@ -242,24 +204,21 @@ namespace Microsoft.FSharp.Collections
let rec existsOpt (f:OptimizedClosures.FSharpFunc<_,_,_>) m =
match m with
- | MapEmpty -> false
- | MapOne(k2,v2) -> f.Invoke(k2, v2)
+ | MapNode(Size=0) -> false
| MapNode(k2,v2,l,r,_) -> existsOpt f l || f.Invoke(k2, v2) || existsOpt f r
let exists f m = existsOpt (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) m
let rec forallOpt (f:OptimizedClosures.FSharpFunc<_,_,_>) m =
match m with
- | MapEmpty -> true
- | MapOne(k2,v2) -> f.Invoke(k2, v2)
+ | MapNode(Size=0) -> true
| MapNode(k2,v2,l,r,_) -> forallOpt f l && f.Invoke(k2, v2) && forallOpt f r
let forall f m = forallOpt (OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)) m
let rec map f m =
match m with
- | MapEmpty -> empty
- | MapOne(k,v) -> MapOne(k,f v)
+ | MapNode(Size=0) -> Constants.Empty
| MapNode(k,v,l,r,h) ->
let l2 = map f l
let v2 = f v
@@ -268,8 +227,7 @@ namespace Microsoft.FSharp.Collections
let rec mapiOpt (f:OptimizedClosures.FSharpFunc<_,_,_>) m =
match m with
- | MapEmpty -> empty
- | MapOne(k,v) -> MapOne(k, f.Invoke(k, v))
+ | MapNode(Size=0) -> Constants.Empty
| MapNode(k,v,l,r,h) ->
let l2 = mapiOpt f l
let v2 = f.Invoke(k, v)
@@ -280,8 +238,7 @@ namespace Microsoft.FSharp.Collections
let rec foldBackOpt (f:OptimizedClosures.FSharpFunc<_,_,_,_>) m x =
match m with
- | MapEmpty -> x
- | MapOne(k,v) -> f.Invoke(k,v,x)
+ | MapNode(Size=0) -> x
| MapNode(k,v,l,r,_) ->
let x = foldBackOpt f r x
let x = f.Invoke(k,v,x)
@@ -291,8 +248,7 @@ namespace Microsoft.FSharp.Collections
let rec foldOpt (f:OptimizedClosures.FSharpFunc<_,_,_,_>) x m =
match m with
- | MapEmpty -> x
- | MapOne(k,v) -> f.Invoke(x,k,v)
+ | MapNode(Size=0) -> x
| MapNode(k,v,l,r,_) ->
let x = foldOpt f x l
let x = f.Invoke(x,k,v)
@@ -300,15 +256,10 @@ namespace Microsoft.FSharp.Collections
let fold f x m = foldOpt (OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(f)) x m
- let foldSectionOpt (comparer: IComparer<'Value>) lo hi (f:OptimizedClosures.FSharpFunc<_,_,_,_>) m x =
+ let foldSectionOpt (comparer:IComparer<'Key>) lo hi (f:OptimizedClosures.FSharpFunc<_,_,_,_>) m x =
let rec foldFromTo (f:OptimizedClosures.FSharpFunc<_,_,_,_>) m x =
match m with
- | MapEmpty -> x
- | MapOne(k,v) ->
- let cLoKey = comparer.Compare(lo,k)
- let cKeyHi = comparer.Compare(k,hi)
- let x = if cLoKey <= 0 && cKeyHi <= 0 then f.Invoke(k, v, x) else x
- x
+ | MapNode(Size=0) -> x
| MapNode(k,v,l,r,_) ->
let cLoKey = comparer.Compare(lo,k)
let cKeyHi = comparer.Compare(k,hi)
@@ -319,107 +270,167 @@ namespace Microsoft.FSharp.Collections
if comparer.Compare(lo,hi) = 1 then x else foldFromTo f m x
- let foldSection (comparer: IComparer<'Value>) lo hi f m x =
+ let foldSection (comparer:IComparer<'Key>) lo hi f m x =
foldSectionOpt comparer lo hi (OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(f)) m x
- let toList m =
- let rec loop m acc =
- match m with
- | MapEmpty -> acc
- | MapOne(k,v) -> (k,v)::acc
- | MapNode(k,v,l,r,_) -> loop l ((k,v)::loop r acc)
- loop m []
- let toArray m = m |> toList |> Array.ofList
- let ofList comparer l = List.fold (fun acc (k,v) -> add comparer k v acc) empty l
-
- let rec mkFromEnumerator comparer acc (e : IEnumerator<_>) =
- if e.MoveNext() then
- let (x,y) = e.Current
- mkFromEnumerator comparer (add comparer x y acc) e
- else acc
+ // create a mapping function which indexes the array, but with duplicate values removed
+ let private getLatestAccessor (comparer:IComparer<'Key>) (keys:IReadOnlyList<'Key>) (array:IReadOnlyList<'T>) =
+ let rec getFirstDuplicateKey i =
+ if i >= array.Count-1 then None
+ elif comparer.Compare (keys.[i], keys.[i+1]) = 0 then Some i
+ else getFirstDuplicateKey (i+1)
+
+ match getFirstDuplicateKey 0 with
+ | None -> (fun i -> array.[i]), array.Count
+ | Some idx ->
+ let indexes = ResizeArray (array.Count-idx)
+ indexes.Add (idx+1)
+ for i = idx+2 to array.Count-1 do
+ if comparer.Compare (keys.[i-1], keys.[i]) = 0 then
+ indexes.[indexes.Count-1] <- i
+ else
+ indexes.Add i
+ (fun i -> if i < idx then array.[i] else array.[indexes.[i-idx]]), idx+indexes.Count
+
+ let constructViaArray comparer (data:seq<'Key*'Value>) =
+ let array = data |> Seq.toArray
+ if array.Length = 0 then Constants.Empty
+ else
+ let keys = array |> Array.map fst
+
+ Microsoft.FSharp.Primitives.Basics.Array.stableSortWithKeysAndComparer comparer array keys
+
+ let getKV, count =
+ getLatestAccessor comparer keys array
+
+ let rec loop lower upper =
+ assert (lower <= upper)
+ let mid = lower + (upper-lower)/2
+ let k,v = getKV mid
+ if mid = upper then
+ mkLeaf k v
+ else
+ let right = loop (mid+1) upper
+ if mid = lower then
+ mk Constants.Empty k v right
+ else
+ let left = loop lower (mid-1)
+ mk left k v right
+
+ loop 0 (count-1)
+
+ []
+ let largeObjectHeapBytes = 85000
+
+ let maxInitializationObjectCount<'Key, 'Value> () =
+ largeObjectHeapBytes * 10 / 9 / sizeof<'Key*'Value> / 2
+
+ let ofSeqlImpl comparer (e:IEnumerator<'Key*'Value>) =
+ if not (e.MoveNext()) then Constants.Empty
+ else
+ let chunk = ResizeArray ()
+ let maxCount = maxInitializationObjectCount<'Key, 'Value> ()
+ let rec populate () =
+ chunk.Add e.Current
+ if chunk.Count = maxCount then true
+ elif e.MoveNext () then populate ()
+ else false
+
+ let more = populate ()
+ let chunkTree = constructViaArray comparer chunk
+
+ if not more then
+ chunkTree
+ else
+ let rec addRemainder acc =
+ if e.MoveNext () then
+ let x, y = e.Current
+ addRemainder (add comparer x y acc)
+ else
+ acc
+ addRemainder chunkTree
+
+ let ofList comparer (l:list<'Key*'Value>) =
+ match l with
+ | [] -> Constants.Empty
+ | l when (List.length l) <= (maxInitializationObjectCount<'Key, 'Value> ()) -> constructViaArray comparer l
+ | _ -> ofSeqlImpl comparer ((l:>seq<_>).GetEnumerator ())
let ofArray comparer (arr : array<_>) =
- let mutable res = empty
- for (x,y) in arr do
- res <- add comparer x y res
- res
+ constructViaArray comparer arr
let ofSeq comparer (c : seq<'Key * 'T>) =
match c with
| :? array<'Key * 'T> as xs -> ofArray comparer xs
| :? list<'Key * 'T> as xs -> ofList comparer xs
- | _ ->
- use ie = c.GetEnumerator()
- mkFromEnumerator comparer empty ie
-
+ | _ -> ofSeqlImpl comparer (c.GetEnumerator())
let copyToArray s (arr: _[]) i =
let j = ref i
s |> iter (fun x y -> arr.[!j] <- KeyValuePair(x,y); j := !j + 1)
-
-
- /// Imperative left-to-right iterators.
- []
- type MapIterator<'Key,'Value when 'Key : comparison > =
- { /// invariant: always collapseLHS result
- mutable stack: MapTree<'Key,'Value> list;
- /// true when MoveNext has been called
- mutable started : bool }
-
- // collapseLHS:
- // a) Always returns either [] or a list starting with MapOne.
- // b) The "fringe" of the set stack is unchanged.
- let rec collapseLHS stack =
- match stack with
- | [] -> []
- | MapEmpty :: rest -> collapseLHS rest
- | MapOne _ :: _ -> stack
- | (MapNode(k,v,l,r,_)) :: rest -> collapseLHS (l :: MapOne (k,v) :: r :: rest)
- let mkIterator s = { stack = collapseLHS [s]; started = false }
+ []
+ type TreeIterator<'Key,'Value when 'Key : comparison>(root) =
+ let items = ResizeArray> ()
+ let mutable depth = 0
+
+ let rec tryPush item =
+ if size item > 0 then
+ if depth = items.Count then
+ items.Add item
+ else
+ items.[depth] <- item
+ depth <- depth + 1
+
+ tryPush (left item)
+
+ do tryPush root
+
+ member inline __.PopCurrent f =
+ depth <- depth - 1
+ match items.[depth] with
+ | MapNode(k,v,_,r,_) ->
+ tryPush r
+ f k v
+
+ member __.MorePairs = depth > 0
+
+ let inline private getEnumerable f m =
+ seq { let iterator = TreeIterator<_,_> m
+ while iterator.MorePairs do
+ yield iterator.PopCurrent f }
+
+ let tryGetSmallEnumerable m =
+ match m with
+ | MapNode(Size=0) -> Seq.empty
+ | MapNode(k,v,_,_,1) -> [ KeyValuePair (k,v) ] :> seq<_>
+ | MapNode(k,v,MapNode(lk,lv,_,_,1),_,2) -> [| KeyValuePair (lk,lv); KeyValuePair (k,v) |] :> seq<_>
+ | MapNode(k,v,_,MapNode(rk,rv,_,_,1),2) -> [| KeyValuePair (k,v); KeyValuePair (rk,rv) |] :> seq<_>
+ | MapNode(k,v,MapNode(lk,lv,_,_,1),MapNode(rk,rv,_,_,1),3) -> [| KeyValuePair (lk,lv); KeyValuePair (k,v); KeyValuePair (rk,rv) |] :> seq<_>
+ | _ -> null
- let notStarted() = raise (InvalidOperationException(SR.GetString(SR.enumerationNotStarted)))
- let alreadyFinished() = raise (InvalidOperationException(SR.GetString(SR.enumerationAlreadyFinished)))
+ let getKVEnumerable m =
+ match tryGetSmallEnumerable m with
+ | null -> getEnumerable (fun k v -> KeyValuePair (k, v)) m
+ | small -> small
- let current i =
- if i.started then
- match i.stack with
- | MapOne (k,v) :: _ -> new KeyValuePair<_,_>(k,v)
- | [] -> alreadyFinished()
- | _ -> failwith "Please report error: Map iterator, unexpected stack for current"
- else
- notStarted()
-
- let rec moveNext i =
- if i.started then
- match i.stack with
- | MapOne _ :: rest -> i.stack <- collapseLHS rest
- not i.stack.IsEmpty
- | [] -> false
- | _ -> failwith "Please report error: Map iterator, unexpected stack for moveNext"
- else
- i.started <- true (* The first call to MoveNext "starts" the enumeration. *)
- not i.stack.IsEmpty
-
- let mkIEnumerator s =
- let i = ref (mkIterator s)
- { new IEnumerator<_> with
- member __.Current = current !i
- interface System.Collections.IEnumerator with
- member __.Current = box (current !i)
- member __.MoveNext() = moveNext !i
- member __.Reset() = i := mkIterator s
- interface System.IDisposable with
- member __.Dispose() = ()}
+ let toSeq m =
+ getEnumerable (fun k v -> k, v) m
+ let toList m =
+ let iterator = TreeIterator<_,_> m
+ List.init (size m) (fun _ -> iterator.PopCurrent (fun k v -> k, v))
+ let toArray m =
+ let iterator = TreeIterator<_,_> m
+ Array.init (size m) (fun _ -> iterator.PopCurrent (fun k v -> k, v))
[>)>]
[]
[]
[]
[]
- type Map<[]'Key,[]'Value when 'Key : comparison >(comparer: IComparer<'Key>, tree: MapTree<'Key,'Value>) =
+ type Map<[]'Key,[]'Value when 'Key : comparison >(comparer:IComparer<'Key>, tree: MapTree<'Key,'Value>) =
#if !FX_NO_BINARY_SERIALIZATION
[]
@@ -441,7 +452,7 @@ namespace Microsoft.FSharp.Collections
// set (it is just a lookup into a .NET table of type-instantiation-indexed static fields).
static let empty =
let comparer = LanguagePrimitives.FastGenericComparer<'Key>
- new Map<'Key,'Value>(comparer,MapTree<_,_>.MapEmpty)
+ new Map<'Key,'Value>(comparer,MapTree.Constants.Empty)
#if !FX_NO_BINARY_SERIALIZATION
[]
@@ -539,6 +550,8 @@ namespace Microsoft.FSharp.Collections
#endif
MapTree.tryFind comparer key tree
+ member m.ToSeq() = MapTree.toSeq tree
+
member m.ToList() = MapTree.toList tree
member m.ToArray() = MapTree.toArray tree
@@ -570,10 +583,10 @@ namespace Microsoft.FSharp.Collections
override this.GetHashCode() = this.ComputeHashCode()
interface IEnumerable> with
- member __.GetEnumerator() = MapTree.mkIEnumerator tree
+ member __.GetEnumerator() = (MapTree.getKVEnumerable tree).GetEnumerator()
interface System.Collections.IEnumerable with
- member __.GetEnumerator() = (MapTree.mkIEnumerator tree :> System.Collections.IEnumerator)
+ member __.GetEnumerator() = (((MapTree.getKVEnumerable tree).GetEnumerator()) :> System.Collections.IEnumerator)
interface IDictionary<'Key, 'Value> with
member m.Item
@@ -710,7 +723,7 @@ namespace Microsoft.FSharp.Collections
let foldBack<'Key,'T,'State when 'Key : comparison> folder (table:Map<'Key,'T>) (state:'State) = MapTree.foldBack folder table.Tree state
[]
- let toSeq (table:Map<_,_>) = table |> Seq.map (fun kvp -> kvp.Key, kvp.Value)
+ let toSeq (table:Map<_,_>) = table.ToSeq()
[]
let findKey predicate (table : Map<_,_>) = table |> toSeq |> Seq.pick (fun (k,v) -> if predicate k v then Some(k) else None)
diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs
index d04a8f8b45c..14e82785bdc 100644
--- a/src/fsharp/FSharp.Core/prim-types.fs
+++ b/src/fsharp/FSharp.Core/prim-types.fs
@@ -770,8 +770,548 @@ namespace Microsoft.FSharp.Core
let anyToStringShowingNull x = anyToString "null" x
- module HashCompare =
-
+ module internal Reflection =
+ let inline flagsOr<'a> (lhs:'a) (rhs:'a) =
+ (# "or" lhs rhs : 'a #)
+
+ let inline flagsAnd<'a> (lhs:'a) (rhs:'a) =
+ (# "and" lhs rhs : 'a #)
+
+ let inline flagsContains<'a when 'a : equality> (flags:'a) (mask:'a) (value:'a) =
+ (flagsAnd flags mask).Equals value
+
+ let inline flagsIsSet<'a when 'a : equality> (flags:'a) (value:'a) =
+ flagsContains flags value value
+
+#if FX_RESHAPED_REFLECTION
+ module internal ReflectionAdapters =
+ let toArray<'a> (s:System.Collections.Generic.IEnumerable<'a>) =
+ (System.Collections.Generic.List<'a> s).ToArray ()
+
+ open System
+
+ let inline hasFlag (flag : BindingFlags) f = flagsIsSet f flag
+ let isDeclaredFlag f = hasFlag BindingFlags.DeclaredOnly f
+ let isPublicFlag f = hasFlag BindingFlags.Public f
+ let isStaticFlag f = hasFlag BindingFlags.Static f
+ let isInstanceFlag f = hasFlag BindingFlags.Instance f
+ let isNonPublicFlag f = hasFlag BindingFlags.NonPublic f
+
+ let isAcceptable bindingFlags isStatic isPublic =
+ // 1. check if member kind (static\instance) was specified in flags
+ ((isStaticFlag bindingFlags && isStatic) || (isInstanceFlag bindingFlags && not isStatic)) &&
+ // 2. check if member accessibility was specified in flags
+ ((isPublicFlag bindingFlags && isPublic) || (isNonPublicFlag bindingFlags && not isPublic))
+
+ type System.Type with
+ member this.GetNestedType (name, bindingFlags) =
+ // MSDN: http://msdn.microsoft.com/en-us/library/0dcb3ad5.aspx
+ // The following BindingFlags filter flags can be used to define which nested types to include in the search:
+ // You must specify either BindingFlags.Public or BindingFlags.NonPublic to get a return.
+ // Specify BindingFlags.Public to include public nested types in the search.
+ // Specify BindingFlags.NonPublic to include non-public nested types (that is, private, internal, and protected nested types) in the search.
+ // This method returns only the nested types of the current type. It does not search the base classes of the current type.
+ // To find types that are nested in base classes, you must walk the inheritance hierarchy, calling GetNestedType at each level.
+ let e = this.GetTypeInfo().DeclaredNestedTypes.GetEnumerator ()
+ let rec f () =
+ if not (e.MoveNext ()) then null
+ else
+ let nestedTy = e.Current
+ if (String.Equals (nestedTy.Name, name)) &&
+ ((isPublicFlag bindingFlags && nestedTy.IsNestedPublic) ||
+ (isNonPublicFlag bindingFlags && (nestedTy.IsNestedPrivate || nestedTy.IsNestedFamily || nestedTy.IsNestedAssembly || nestedTy.IsNestedFamORAssem || nestedTy.IsNestedFamANDAssem))) then
+ nestedTy.AsType ()
+ else
+ f ()
+ f ()
+
+ // use different sources based on Declared flag
+ member this.GetMethods (bindingFlags) =
+ let methods =
+ if isDeclaredFlag bindingFlags then
+ this.GetTypeInfo().DeclaredMethods
+ else
+ this.GetRuntimeMethods()
+
+ Array.FindAll (toArray methods, Predicate (fun m ->
+ isAcceptable bindingFlags m.IsStatic m.IsPublic))
+
+ // use different sources based on Declared flag
+ member this.GetFields (bindingFlags) =
+ let fields =
+ if isDeclaredFlag bindingFlags then
+ this.GetTypeInfo().DeclaredFields
+ else
+ this.GetRuntimeFields()
+
+ Array.FindAll (toArray fields, Predicate (fun f ->
+ isAcceptable bindingFlags f.IsStatic f.IsPublic))
+
+ // use different sources based on Declared flag
+ member this.GetProperties (bindingFlags) =
+ let properties =
+ if isDeclaredFlag bindingFlags then
+ this.GetTypeInfo().DeclaredProperties
+ else
+ this.GetRuntimeProperties ()
+
+ Array.FindAll (toArray properties, Predicate (fun pi ->
+ let mi =
+ match pi.GetMethod with
+ | null -> pi.SetMethod
+ | _ -> pi.GetMethod
+ if obj.ReferenceEquals (mi, null) then
+ false
+ else
+ isAcceptable bindingFlags mi.IsStatic mi.IsPublic))
+
+ member this.IsGenericTypeDefinition = this.GetTypeInfo().IsGenericTypeDefinition
+
+ type System.Reflection.MemberInfo with
+ member this.GetCustomAttributes(attrTy, inherits) : obj[] =
+ downcast box(toArray (CustomAttributeExtensions.GetCustomAttributes(this, attrTy, inherits)))
+
+ module internal SystemAdapters =
+ type Converter<'TInput, 'TOutput> = delegate of 'TInput -> 'TOutput
+
+ type System.Array with
+ static member ConvertAll<'TInput, 'TOutput>(input:'TInput[], conv:Converter<'TInput, 'TOutput>) =
+ let output = (# "newarr !0" type ('TOutput) input.Length : 'TOutput array #)
+ for i = 0 to input.Length-1 do
+ set output i (conv.Invoke (get input i))
+ output
+
+ open PrimReflectionAdapters
+ open ReflectionAdapters
+ open SystemAdapters
+#endif
+
+
+#if FX_RESHAPED_REFLECTION
+ let instancePropertyFlags = BindingFlags.Instance
+ let staticFieldFlags = BindingFlags.Static
+ let staticMethodFlags = BindingFlags.Static
+#else
+ let instancePropertyFlags = flagsOr BindingFlags.GetProperty BindingFlags.Instance
+ let staticFieldFlags = flagsOr BindingFlags.GetField BindingFlags.Static
+ let staticMethodFlags = BindingFlags.Static
+#endif
+
+ let tupleNames = [|
+ "System.Tuple`1"; "System.Tuple`2"; "System.Tuple`3";
+ "System.Tuple`4"; "System.Tuple`5"; "System.Tuple`6";
+ "System.Tuple`7"; "System.Tuple`8"; "System.Tuple"
+ "System.ValueTuple`1"; "System.ValueTuple`2"; "System.ValueTuple`3";
+ "System.ValueTuple`4"; "System.ValueTuple`5"; "System.ValueTuple`6";
+ "System.ValueTuple`7"; "System.ValueTuple`8"; "System.ValueTuple" |]
+
+ let simpleTupleNames = [|
+ "Tuple`1"; "Tuple`2"; "Tuple`3";
+ "Tuple`4"; "Tuple`5"; "Tuple`6";
+ "Tuple`7"; "Tuple`8";
+ "ValueTuple`1"; "ValueTuple`2"; "ValueTuple`3";
+ "ValueTuple`4"; "ValueTuple`5"; "ValueTuple`6";
+ "ValueTuple`7"; "ValueTuple`8"; |]
+
+ let isTupleType (typ:Type) =
+ // We need to be careful that we only rely typ.IsGenericType, typ.Namespace and typ.Name here.
+ //
+ // Historically the FSharp.Core reflection utilities get used on implementations of
+ // System.Type that don't have functionality such as .IsEnum and .FullName fully implemented.
+ // This happens particularly over TypeBuilderInstantiation types in the ProvideTypes implementation of System.TYpe
+ // used in F# type providers.
+ typ.IsGenericType &&
+ System.String.Equals(typ.Namespace, "System") &&
+ Array.Exists (simpleTupleNames, Predicate typ.Name.StartsWith)
+
+#if !FX_NO_REFLECTION_ONLY
+ let assemblyName = typeof.Assembly.GetName().Name
+ let _ = assert (System.String.Equals (assemblyName, "FSharp.Core"))
+ let cmaName = typeof.FullName
+
+ let tryFindCompilationMappingAttributeFromData (attrs:System.Collections.Generic.IList, res:byref) : bool =
+ match attrs with
+ | null -> false
+ | _ ->
+ let mutable found = false
+ for a in attrs do
+ if a.Constructor.DeclaringType.FullName.Equals cmaName then
+ let args = a.ConstructorArguments
+ let flags =
+ match args.Count with
+ | 1 -> ((let x = args.[0] in x.Value :?> SourceConstructFlags), 0, 0)
+ | 2 -> ((let x = args.[0] in x.Value :?> SourceConstructFlags), (let x = args.[1] in x.Value :?> int), 0)
+ | 3 -> ((let x = args.[0] in x.Value :?> SourceConstructFlags), (let x = args.[1] in x.Value :?> int), (let x = args.[2] in x.Value :?> int))
+ | _ -> (SourceConstructFlags.None, 0, 0)
+ res <- flags
+ found <- true
+ found
+
+ let findCompilationMappingAttributeFromData attrs =
+ let mutable x = unsafeDefault<_>
+ match tryFindCompilationMappingAttributeFromData (attrs, &x) with
+ | false -> raise (Exception "no compilation mapping attribute")
+ | true -> x
+#endif
+ let hasCustomEquality (typ:Type) =
+ let arr = typ.GetCustomAttributes (typeof, false)
+ arr.Length > 0
+
+ let hasCustomComparison (typ:Type) =
+ let arr = typ.GetCustomAttributes (typeof, false)
+ arr.Length > 0
+
+ let tryFindCompilationMappingAttribute (attrs:obj[], res:byref) : bool =
+ match attrs with
+ | null | [||] -> false
+ | [| :? CompilationMappingAttribute as a |] ->
+ res <- a.SourceConstructFlags, a.SequenceNumber, a.VariantNumber
+ true
+ | _ -> raise (System.InvalidOperationException (SR.GetString(SR.multipleCompilationMappings)))
+
+ let findCompilationMappingAttribute (attrs:obj[]) =
+ let mutable x = unsafeDefault<_>
+ match tryFindCompilationMappingAttribute (attrs, &x) with
+ | false -> raise (Exception "no compilation mapping attribute")
+ | true -> x
+
+ let tryFindCompilationMappingAttributeFromType (typ:Type, res:byref) : bool =
+#if !FX_NO_REFLECTION_ONLY
+ let assem = typ.Assembly
+ if (not (obj.ReferenceEquals(assem, null))) && assem.ReflectionOnly then
+ tryFindCompilationMappingAttributeFromData (typ.GetCustomAttributesData(), &res)
+ else
+#endif
+ tryFindCompilationMappingAttribute (typ.GetCustomAttributes (typeof,false), &res)
+
+ let tryFindCompilationMappingAttributeFromMemberInfo (info:MemberInfo, res:byref) : bool =
+#if !FX_NO_REFLECTION_ONLY
+ let assem = info.DeclaringType.Assembly
+ if (not (obj.ReferenceEquals (assem, null))) && assem.ReflectionOnly then
+ tryFindCompilationMappingAttributeFromData (info.GetCustomAttributesData(), &res)
+ else
+#endif
+ tryFindCompilationMappingAttribute (info.GetCustomAttributes (typeof,false), &res)
+
+ let findCompilationMappingAttributeFromMemberInfo (info:MemberInfo) =
+#if !FX_NO_REFLECTION_ONLY
+ let assem = info.DeclaringType.Assembly
+ if (not (obj.ReferenceEquals (assem, null))) && assem.ReflectionOnly then
+ findCompilationMappingAttributeFromData (info.GetCustomAttributesData())
+ else
+#endif
+ findCompilationMappingAttribute (info.GetCustomAttributes (typeof,false))
+
+ let tryFindSourceConstructFlagsOfType (typ:Type, res:byref) : bool =
+ let mutable x = unsafeDefault<_>
+ if tryFindCompilationMappingAttributeFromType (typ, &x) then
+ let flags,_n,_vn = x
+ res <- flags
+ true
+ else
+ false
+
+ let isKnownType (typ:Type, bindingFlags:BindingFlags, knownType:SourceConstructFlags) =
+ let mutable flags = unsafeDefault<_>
+ match tryFindSourceConstructFlagsOfType (typ, &flags) with
+ | false -> false
+ | true ->
+ (flagsContains flags SourceConstructFlags.KindMask knownType) &&
+ // We see private representations only if BindingFlags.NonPublic is set
+ (if flagsIsSet flags SourceConstructFlags.NonPublicRepresentation then
+ flagsIsSet bindingFlags BindingFlags.NonPublic
+ else
+ true)
+
+ let isRecordType (typ:Type, bindingFlags:BindingFlags) = isKnownType (typ, bindingFlags, SourceConstructFlags.RecordType)
+ let isObjectType (typ:Type, bindingFlags:BindingFlags) = isKnownType (typ, bindingFlags, SourceConstructFlags.ObjectType)
+ let isUnionType (typ:Type, bindingFlags:BindingFlags) = isKnownType (typ, bindingFlags, SourceConstructFlags.SumType)
+
+ let isFieldProperty (prop : PropertyInfo) =
+ let mutable res = unsafeDefault<_>
+ match tryFindCompilationMappingAttributeFromMemberInfo(prop:>MemberInfo, &res) with
+ | false -> false
+ | true ->
+ let (flags,_n,_vn) = res
+ flagsContains flags SourceConstructFlags.KindMask SourceConstructFlags.Field
+
+ let sequenceNumberOfMember (x:MemberInfo) = let (_,n,_) = findCompilationMappingAttributeFromMemberInfo x in n
+ let variantNumberOfMember (x:MemberInfo) = let (_,_,vn) = findCompilationMappingAttributeFromMemberInfo x in vn
+
+ // Although this funciton is called sortFreshArray (and was so in it's previously life in reflect.fs)
+ // it does not create a fresh array, but rather uses the existing array.
+ let sortFreshArray (f:'a->int) (arr:'a[]) =
+ let comparer = System.Collections.Generic.Comparer.Default
+ System.Array.Sort (arr, {
+ new IComparer<'a> with
+ member __.Compare (lhs:'a, rhs:'a) =
+ comparer.Compare (f lhs, f rhs) })
+ arr
+
+ let fieldPropsOfRecordType (typ:Type, bindingFlags) =
+ let properties = typ.GetProperties (flagsOr instancePropertyFlags bindingFlags)
+ let fields = System.Array.FindAll (properties, Predicate isFieldProperty)
+ sortFreshArray sequenceNumberOfMember fields
+
+ let getUnionTypeTagNameMap (typ:Type,bindingFlags:BindingFlags) : (int*string)[] =
+ let enumTyp = typ.GetNestedType ("Tags", bindingFlags)
+ // Unions with a singleton case do not get a Tags type (since there is only one tag), hence enumTyp may be null in this case
+ match enumTyp with
+ | null ->
+ let methods = typ.GetMethods (flagsOr staticMethodFlags bindingFlags)
+ let maybeTagNames =
+ Array.ConvertAll (methods, Converter (fun minfo ->
+ let mutable res = unsafeDefault<_>
+ match tryFindCompilationMappingAttributeFromMemberInfo (minfo:>MemberInfo, &res) with
+ | false -> unsafeDefault<_>
+ | true ->
+ let flags,n,_vn = res
+ if flagsContains flags SourceConstructFlags.KindMask SourceConstructFlags.UnionCase then
+ // chop "get_" or "New" off the front
+ let nm =
+ let nm = minfo.Name
+ if nm.StartsWith "get_" then nm.Substring 4
+ elif nm.StartsWith "New" then nm.Substring 3
+ else nm
+ (n, nm)
+ else
+ unsafeDefault<_> ))
+ Array.FindAll (maybeTagNames, Predicate (fun maybeTagName -> not (obj.ReferenceEquals (maybeTagName, null))))
+ | _ ->
+ let fields = enumTyp.GetFields (flagsOr staticFieldFlags bindingFlags)
+ let filtered = Array.FindAll (fields, (fun (f:FieldInfo) -> f.IsStatic && f.IsLiteral))
+ let sorted = sortFreshArray (fun (f:FieldInfo) -> (f.GetValue null) :?> int) filtered
+ Array.ConvertAll (sorted, Converter (fun tagfield -> (tagfield.GetValue(null) :?> int),tagfield.Name))
+
+ let getUnionCasesTyp (typ: Type, _bindingFlags) =
+#if CASES_IN_NESTED_CLASS
+ let casesTyp = typ.GetNestedType("Cases", bindingFlags)
+ if casesTyp.IsGenericTypeDefinition then casesTyp.MakeGenericType(typ.GetGenericArguments())
+ else casesTyp
+#else
+ typ
+#endif
+
+ let getUnionCaseTyp (typ: Type, tag: int, bindingFlags) =
+ let tagFields = getUnionTypeTagNameMap(typ,bindingFlags)
+ let tagField = let _,f = Array.Find (tagFields, Predicate (fun (i,_) -> i = tag)) in f
+
+ if tagFields.Length = 1 then
+ typ
+ else
+ // special case: two-cased DU annotated with CompilationRepresentation(UseNullAsTrueValue)
+ // in this case it will be compiled as one class: return self type for non-nullary case and null for nullary
+ let isTwoCasedDU =
+ if tagFields.Length = 2 then
+ match typ.GetCustomAttributes(typeof, false) with
+ | [|:? CompilationRepresentationAttribute as attr|] ->
+ flagsIsSet attr.Flags CompilationRepresentationFlags.UseNullAsTrueValue
+ | _ -> false
+ else
+ false
+ if isTwoCasedDU then
+ typ
+ else
+ let casesTyp = getUnionCasesTyp (typ, bindingFlags)
+ let caseTyp = casesTyp.GetNestedType(tagField, bindingFlags) // if this is null then the union is nullary
+ match caseTyp with
+ | null -> null
+ | _ when caseTyp.IsGenericTypeDefinition -> caseTyp.MakeGenericType(casesTyp.GetGenericArguments())
+ | _ -> caseTyp
+
+ let fieldsPropsOfUnionCase(typ:Type, tag:int, bindingFlags) =
+ // Lookup the type holding the fields for the union case
+ let caseTyp = getUnionCaseTyp (typ, tag, bindingFlags)
+ let caseTyp = match caseTyp with null -> typ | _ -> caseTyp
+ let properties = caseTyp.GetProperties (flagsOr instancePropertyFlags bindingFlags)
+ let filtered = Array.FindAll (properties, Predicate (fun p -> if isFieldProperty p then (variantNumberOfMember (p:>MemberInfo)) = tag else false))
+ sortFreshArray (fun (p:PropertyInfo) -> sequenceNumberOfMember p) filtered
+
+ let getAllInstanceFields (typ:Type) =
+ let fields = typ.GetFields (flagsOr BindingFlags.Instance (flagsOr BindingFlags.Public BindingFlags.NonPublic))
+ Array.ConvertAll (fields, Converter (fun p -> p.FieldType))
+
+ module HashCompare =
+#if FX_RESHAPED_REFLECTION
+ open Reflection.SystemAdapters
+#endif
+ let isArray (ty:Type) =
+ ty.IsArray || (typeof.IsAssignableFrom ty)
+
+ let canUseDotnetDefaultComparisonOrEquality isCustom hasStructuralInterface stringsRequireHandling er (rootType:Type) =
+ let processed = System.Collections.Generic.HashSet ()
+
+ let bindingPublicOrNonPublic =
+ Reflection.flagsOr BindingFlags.Public BindingFlags.NonPublic
+
+ let rec isSuitableNullableTypeOrNotNullable (ty:Type) =
+ // although nullables not explicitly handled previously, they need special handling
+ // due to the implicit casting to their underlying generic type (i.e. could be a float)
+ let isNullableType =
+ ty.IsGenericType
+ && ty.GetGenericTypeDefinition().Equals typedefof>
+ if isNullableType then
+ checkType 0 (ty.GetGenericArguments ())
+ else
+ true
+
+ and isSuitableTupleType (ty:Type) =
+ ty.IsValueType && // Tuple<...> don't have implementation, but ValueTuple<...> does
+ Reflection.isTupleType ty &&
+ checkType 0 (ty.GetGenericArguments ())
+
+ and isSuitableStructType (ty:Type) =
+ ty.IsValueType &&
+ Reflection.isObjectType (ty, bindingPublicOrNonPublic) &&
+ (not (isCustom ty)) &&
+ checkType 0 (Reflection.getAllInstanceFields ty)
+
+ and isSuitableRecordType (ty:Type) =
+ Reflection.isRecordType (ty, bindingPublicOrNonPublic) &&
+ (not (isCustom ty)) &&
+ ( let fields = Reflection.fieldPropsOfRecordType (ty, bindingPublicOrNonPublic)
+ let fieldTypes = Array.ConvertAll (fields, Converter (fun f -> f.PropertyType))
+ checkType 0 fieldTypes)
+
+ and isSuitableUnionType (ty:Type) =
+ Reflection.isUnionType (ty, bindingPublicOrNonPublic) &&
+ (not (isCustom ty)) &&
+ ( let cases = Reflection.getUnionTypeTagNameMap (ty, bindingPublicOrNonPublic)
+ let rec checkCases idx =
+ if idx = cases.Length then true
+ else
+ let tag,_ = get cases idx
+ let fields = Reflection.fieldsPropsOfUnionCase (ty, tag, bindingPublicOrNonPublic)
+ let fieldTypes = Array.ConvertAll (fields, Converter (fun f -> f.PropertyType))
+ if checkType 0 fieldTypes then
+ checkCases (idx+1)
+ else
+ false
+ checkCases 0)
+
+ and checkType idx (types:Type[]) =
+ if idx = types.Length then true
+ else
+ let ty = get types idx
+ if not (processed.Add ty) then
+ checkType (idx+1) types
+ else
+ ty.IsSealed // covers enum and value types; derived ref types might implement from hasStructuralInterface
+ && not (isArray ty)
+ && (not stringsRequireHandling || (not (ty.Equals typeof)))
+ && (er || (not (ty.Equals typeof)))
+ && (er || (not (ty.Equals typeof)))
+ && isSuitableNullableTypeOrNotNullable ty
+ && ((not (hasStructuralInterface ty))
+ || isSuitableTupleType ty
+ || isSuitableStructType ty
+ || isSuitableRecordType ty
+ || isSuitableUnionType ty)
+ && checkType (idx+1) types
+
+ checkType 0 [|rootType|]
+
+ //-------------------------------------------------------------------------
+ // LanguagePrimitives.HashCompare: HASHING.
+ //-------------------------------------------------------------------------
+ let defaultHashNodes = 18
+
+ /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality.
+ type CountLimitedHasherPER(sz:int) =
+ []
+ val mutable nodeCount : int
+
+ member x.Fresh() =
+ if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then
+ x
+ else
+ new CountLimitedHasherPER(sz)
+
+ interface IEqualityComparer
+
+ /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality.
+ type UnlimitedHasherER() =
+ interface IEqualityComparer
+
+ /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality.
+ type UnlimitedHasherPER() =
+ interface IEqualityComparer
+
+
+ /// The unique object for unlimited depth for hashing and ER semantics for equality.
+ let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER()
+
+ /// The unique object for unlimited depth for hashing and PER semantics for equality.
+ let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER()
+
+ let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr
+
+ let inline ArrayHashing<'element,'array when 'array :> System.Array> get lowerBound (f:'element->int) (x:'array) : int =
+ let rec loop acc i =
+ if i < lowerBound then acc
+ else loop (HashCombine i acc (f (get x i))) (i-1)
+
+ let lastIdx =
+ let upperBound = lowerBound+defaultHashNodes
+ match lowerBound+x.Length-1 with
+ | oversized when oversized > upperBound -> upperBound
+ | good -> good
+
+ loop 0 lastIdx
+
+ let GenericHashObjArray (iec:System.Collections.IEqualityComparer) (x:obj[]) = ArrayHashing get 0 iec.GetHashCode x
+ let GenericHashByteArray (x:byte[]) = ArrayHashing get 0 intOfByte x
+ let GenericHashInt32Array (x:int32[]) = ArrayHashing get 0 (fun x -> x) x
+ let GenericHashInt64Array (x:int64[]) = ArrayHashing get 0 int32 x
+
+ // special case - arrays do not by default have a decent structural hashing function
+ let GenericHashArbArray (iec : System.Collections.IEqualityComparer) (x: System.Array) : int =
+ match x.Rank with
+ | 1 -> ArrayHashing (fun a i -> a.GetValue i) (x.GetLowerBound 0) iec.GetHashCode x
+ | _ -> HashCombine 10 (x.GetLength(0)) (x.GetLength(1))
+
+ // Core implementation of structural hashing, corresponds to pseudo-code in the
+ // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode().
+ // Arrays are structurally hashed through a separate technique.
+ //
+ // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER.
+ let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int =
+ match x with
+ | null -> 0
+ | (:? System.Array as a) ->
+ // due to the rules of the CLI type system, array casts are "assignment compatible"
+ // see: https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent/
+ // this means that the cast and comparison for byte will also handle sbyte, int32 handle uint32,
+ // and int64 handle uint64. The hash code of an individual array element is different for the different
+ // types, but it is irrelevant for the creation of the hash code - but this is to be replicated in
+ // the tryGetFSharpArrayEqualityComparer function.
+ match a with
+ | :? (obj[]) as oa -> GenericHashObjArray iec oa
+ | :? (byte[]) as ba -> GenericHashByteArray ba
+ | :? (int[]) as ba -> GenericHashInt32Array ba
+ | :? (int64[]) as ba -> GenericHashInt64Array ba
+ | _ -> GenericHashArbArray iec a
+ | :? IStructuralEquatable as a ->
+ a.GetHashCode(iec)
+ | _ ->
+ x.GetHashCode()
+
+ /// Direct call to GetHashCode on the string type
+ let inline HashString (s:string) =
+ match s with
+ | null -> 0
+ | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #)
+
+ // from mscorlib v4.0.30319
+ let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #)
+ let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #)
+ let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #)
+ let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #)
+ let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #)
+ let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #)
+ let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #)
+
+
//-------------------------------------------------------------------------
// LanguagePrimitives.HashCompare: Physical Equality
//-------------------------------------------------------------------------
@@ -790,7 +1330,6 @@ namespace Microsoft.FSharp.Core
let inline PhysicalHashFast (input: 'T) =
PhysicalHashIntrinsic input
-
//-------------------------------------------------------------------------
// LanguagePrimitives.HashCompare: Comparison
//
@@ -806,7 +1345,6 @@ namespace Microsoft.FSharp.Core
let FailGenericComparison (obj: obj) =
raise (new System.ArgumentException(String.Format(SR.GetString(SR.genericCompareFail1), obj.GetType().ToString())))
-
/// This type has two instances - fsComparerER and fsComparerThrow.
/// - fsComparerER = ER semantics = no throw on NaN comparison = new GenericComparer(false) = GenericComparer = GenericComparison
@@ -819,9 +1357,20 @@ namespace Microsoft.FSharp.Core
/// This exception should never be observed by user code.
let NaNException = new System.Exception()
+ let inline ArrayComparison<'T> (f:'T->'T->int) (x:'T[]) (y:'T[]) : int =
+ let lenx = x.Length
+ let leny = y.Length
+ let rec loop c i =
+ if c <> 0 then Math.Sign c
+ elif i = lenx then 0
+ else loop (f (get x i) (get y i)) (i+1)
+ loop (lenx-leny) 0
+
+ let GenericComparisonByteArray (x:byte[]) (y:byte[]) : int = ArrayComparison (fun x y -> (# "conv.i4" x : int32 #)-(# "conv.i4" y : int32 #)) x y
+
/// Implements generic comparison between two objects. This corresponds to the pseudo-code in the F#
/// specification. The treatment of NaNs is governed by "comp".
- let rec GenericCompare (comp:GenericComparer) (xobj:obj,yobj:obj) =
+ let rec GenericCompare (comp:GenericComparer) (xobj:obj,yobj:obj) =
(*if objEq xobj yobj then 0 else *)
match xobj,yobj with
| null,null -> 0
@@ -832,10 +1381,13 @@ namespace Microsoft.FSharp.Core
// Permit structural comparison on arrays
| (:? System.Array as arr1),_ ->
match arr1,yobj with
- // Fast path
- | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericComparisonObjArrayWithComparer comp arr1 arr2
- // Fast path
- | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericComparisonByteArray arr1 arr2
+ | (:? (obj[]) as arr1), (:? (obj[]) as arr2)-> GenericComparisonObjArrayWithComparer comp arr1 arr2
+
+ // The additional equality check is required here because .net treats byte[] and sbyte[] as cast-compatible
+ // (but comparison is different)
+ | (:? (byte[]) as arr1), (:? (byte[]) as arr2)
+ when typeof.Equals (arr1.GetType ()) && typeof.Equals (arr2.GetType ()) -> GenericComparisonByteArray arr1 arr2
+
| _ , (:? System.Array as arr2) -> GenericComparisonArbArrayWithComparer comp arr1 arr2
| _ -> FailGenericComparison xobj
// Check for IStructuralComparable
@@ -1010,46 +1562,211 @@ namespace Microsoft.FSharp.Core
check 0
#endif
- /// optimized case: Core implementation of structural comparison on object arrays.
- and GenericComparisonObjArrayWithComparer (comp:GenericComparer) (x:obj[]) (y:obj[]) : int =
- let lenx = x.Length
- let leny = y.Length
- let c = intOrder lenx leny
- if c <> 0 then c
- else
- let mutable i = 0
- let mutable res = 0
- while i < lenx do
- let c = GenericCompare comp ((get x i), (get y i))
- if c <> 0 then (res <- c; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural comparison on arrays.
- and GenericComparisonByteArray (x:byte[]) (y:byte[]) : int =
- let lenx = x.Length
- let leny = y.Length
- let c = intOrder lenx leny
- if c <> 0 then c
- else
- let mutable i = 0
- let mutable res = 0
- while i < lenx do
- let c = byteOrder (get x i) (get y i)
- if c <> 0 then (res <- c; i <- lenx)
- else i <- i + 1
- res
+ and GenericComparisonObjArrayWithComparer (comp:GenericComparer) (x:obj[]) (y:obj[]) : int =
+ ArrayComparison (fun x y -> GenericCompare comp (x, y)) x y
type GenericComparer with
interface System.Collections.IComparer with
override c.Compare(x:obj,y:obj) = GenericCompare c (x,y)
/// The unique object for comparing values in PER mode (where local exceptions are thrown when NaNs are compared)
- let fsComparerPER = GenericComparer(true)
+ let fsComparerPER = GenericComparer(true)
/// The unique object for comparing values in ER mode (where "0" is returned when NaNs are compared)
let fsComparerER = GenericComparer(false)
+ let isStructuralComparable (ty:Type) = typeof.IsAssignableFrom ty
+ let isValueTypeStructuralComparable (ty:Type) = isStructuralComparable ty && ty.IsValueType
+
+ let canUseDefaultComparer er (rootType:Type) =
+ // "Default" equality for strings is culturally sensitive, so needs special handling
+ canUseDotnetDefaultComparisonOrEquality Reflection.hasCustomComparison isStructuralComparable true er rootType
+
+ type ComparisonUsage =
+ | ERUsage = 0
+ | PERUsage = 1
+ | LessThanUsage = 2
+ | GreaterThanUsage = 3
+
+ []
+ let LessThanUsageReturnFalse = 1
+ []
+ let GreaterThanUsageReturnFalse = -1
+
+ let inline signedComparer<'T> () =
+ box { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ if (# "clt" x y : bool #) then -1
+ else (# "cgt" x y : int #) }
+
+ let inline unsignedComparer<'T> () =
+ box { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ if (# "clt.un" x y : bool #) then -1
+ else (# "cgt.un" x y : int #) }
+
+ let inline floatingPointComparer<'T> onNaN =
+ box { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ if (# "clt" x y : bool #) then -1
+ elif (# "cgt" x y : bool #) then 1
+ elif (# "ceq" x y : bool #) then 0
+ else onNaN () }
+
+ let tryGetFSharpComparer (usage:ComparisonUsage) (externalUse:bool) (ty:Type) : obj =
+ match usage, externalUse, ty with
+ | ComparisonUsage.ERUsage, _, ty when ty.Equals typeof -> box Comparer.Default
+ | ComparisonUsage.ERUsage, _, ty when ty.Equals typeof -> box Comparer.Default
+
+ | ComparisonUsage.PERUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> raise NaNException)
+ | ComparisonUsage.LessThanUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> LessThanUsageReturnFalse)
+ | ComparisonUsage.GreaterThanUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> GreaterThanUsageReturnFalse)
+
+ | ComparisonUsage.PERUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> raise NaNException)
+ | ComparisonUsage.LessThanUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> LessThanUsageReturnFalse)
+ | ComparisonUsage.GreaterThanUsage, _, ty when ty.Equals typeof -> floatingPointComparer (fun () -> GreaterThanUsageReturnFalse)
+
+ // the implemention of Comparer.Default returns a current culture specific comparer
+ | _, _, ty when ty.Equals typeof ->
+ box { new Comparer() with
+ member __.Compare (x,y) =
+ System.String.CompareOrdinal (x, y) }
+
+ | _, _, ty when ty.Equals typeof -> unsignedComparer ()
+ | _, _, ty when ty.Equals typeof -> signedComparer ()
+
+ // these are used as external facing comparers for compatability (they always return -1/0/+1)
+ | _, true, ty when ty.Equals typeof -> unsignedComparer ()
+ | _, true, ty when ty.Equals typeof -> signedComparer ()
+ | _, true, ty when ty.Equals typeof -> signedComparer ()
+ | _, true, ty when ty.Equals typeof -> unsignedComparer ()
+ | _, true, ty when ty.Equals typeof -> unsignedComparer ()
+
+ | _ -> null
+
+ let inline nullableComparer<'a when 'a : null> compare =
+ box { new Comparer<'a>() with
+ member __.Compare (x,y) =
+ match x, y with
+ | null, null -> 0
+ | null, _ -> -1
+ | _, null -> 1
+ | _ -> compare x y }
+
+ let tryGetFSharpArrayComparer (ty:Type) comparer : obj =
+ if ty.Equals typeof then nullableComparer (fun x y -> GenericComparisonObjArrayWithComparer comparer x y)
+ elif ty.Equals typeof then nullableComparer GenericComparisonByteArray
+ else null
+
+ let arrayComparer<'T> comparer =
+ match tryGetFSharpArrayComparer typeof<'T> comparer with
+ | :? Comparer<'T> as arrayComparer -> arrayComparer
+ | _ ->
+ { new Comparer<'T>() with
+ member __.Compare (x, y) =
+ match box x, box y with
+ | null, null -> 0
+ | null, _ -> -1
+ | _, null -> 1
+ | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericComparisonArbArrayWithComparer comparer arr1 arr2
+ | _ -> raise (Exception "invalid logic - expected System.Array") }
+
+ let structuralComparer<'T> comparer =
+ { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ match box x, box y with
+ | null, null -> 0
+ | null, _ -> -1
+ | _, null -> 1
+ | (:? IStructuralComparable as x1), yobj -> x1.CompareTo (yobj, comparer)
+ | _ -> raise (Exception "invalid logic - expected IStructuralEquatable") }
+
+ let structuralComparerValueType<'T> comparer =
+ { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ ((box x):?>IStructuralComparable).CompareTo (y, comparer) }
+
+ let unknownComparer<'T> comparer =
+ { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ GenericCompare comparer (box x, box y) }
+
+ // this wrapper is used with the comparison operators to cause a false result when a NaNException
+ // has been thrown somewhere in the tested objects hierarchy
+ let maybeNaNExceptionComparer<'T> (comparer:Comparer<'T>) valueToCauseFalse =
+ { new Comparer<'T>() with
+ member __.Compare (x,y) =
+ try
+ comparer.Compare (x,y)
+ with
+ e when System.Runtime.CompilerServices.RuntimeHelpers.Equals(e, NaNException) -> valueToCauseFalse }
+
+ let getGenericComparison<'T> usage externalUse =
+ let er = match usage with ComparisonUsage.ERUsage -> true | _ -> false
+
+ match tryGetFSharpComparer usage externalUse typeof<'T> with
+ | :? Comparer<'T> as comparer -> comparer
+ | _ when canUseDefaultComparer er typeof<'T> -> Comparer<'T>.Default
+ | _ ->
+ if er then
+ if isArray typeof<'T> then arrayComparer fsComparerER
+ elif isValueTypeStructuralComparable typeof<'T> then structuralComparerValueType fsComparerER
+ elif isStructuralComparable typeof<'T> then structuralComparer fsComparerER
+ else unknownComparer fsComparerER
+ else
+ let comparer =
+ if isArray typeof<'T> then arrayComparer fsComparerPER
+ elif isValueTypeStructuralComparable typeof<'T> then structuralComparerValueType fsComparerPER
+ elif isStructuralComparable typeof<'T> then structuralComparer fsComparerPER
+ else unknownComparer fsComparerPER
+
+ match usage with
+ | ComparisonUsage.LessThanUsage -> maybeNaNExceptionComparer comparer LessThanUsageReturnFalse
+ | ComparisonUsage.GreaterThanUsage -> maybeNaNExceptionComparer comparer GreaterThanUsageReturnFalse
+ | _ -> comparer
+
+ /// As an optimization, determine if a fast unstable sort can be used with equivalent results
+ let equivalentForStableAndUnstableSort (ty:Type) =
+ ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+ || ty.Equals(typeof)
+
+ []
+ type FSharpComparer_ER<'T> private () =
+ static let comparer = getGenericComparison<'T> ComparisonUsage.ERUsage true
+ static member Comparer = comparer
+
+ []
+ type FSharpComparer_InternalUse_ER<'T> private () =
+ static let equivalentForStableAndUnstableSort = equivalentForStableAndUnstableSort typeof<'T>
+ static let comparer = getGenericComparison<'T> ComparisonUsage.ERUsage false
+ static member Comparer = comparer
+ static member EquivalentForStableAndUnstableSort = equivalentForStableAndUnstableSort
+
+ []
+ type FSharpComparer_PER<'T> private () =
+ static let comparer = getGenericComparison<'T> ComparisonUsage.PERUsage false
+ static member Comparer = comparer
+
+ []
+ type FSharpComparer_ForLessThanComparison<'T> private () =
+ static let comparer = getGenericComparison<'T> ComparisonUsage.LessThanUsage false
+ static member Comparer = comparer
+
+ []
+ type FSharpComparer_ForGreaterThanComparison<'T> private () =
+ static let comparer = getGenericComparison<'T> ComparisonUsage.GreaterThanUsage false
+ static member Comparer = comparer
+
/// Compare two values of the same generic type, using "comp".
//
// "comp" is assumed to be either fsComparerPER or fsComparerER (and hence 'Compare' is implemented via 'GenericCompare').
@@ -1057,7 +1774,12 @@ namespace Microsoft.FSharp.Core
// NOTE: the compiler optimizer is aware of this function and devirtualizes in the
// cases where it is known how a particular type implements generic comparison.
let GenericComparisonWithComparerIntrinsic<'T> (comp:System.Collections.IComparer) (x:'T) (y:'T) : int =
- comp.Compare(box x, box y)
+ if obj.ReferenceEquals (comp, fsComparerER) then
+ FSharpComparer_InternalUse_ER.Comparer.Compare (x, y)
+ elif obj.ReferenceEquals (comp, fsComparerPER) then
+ FSharpComparer_PER.Comparer.Compare (x, y)
+ else
+ comp.Compare (box x, box y)
/// Compare two values of the same generic type, in either PER or ER mode, but include static optimizations
/// for various well-known cases.
@@ -1093,51 +1815,28 @@ namespace Microsoft.FSharp.Core
System.String.CompareOrdinal((# "" x : string #) ,(# "" y : string #))
when 'T : decimal = System.Decimal.Compare((# "" x:decimal #), (# "" y:decimal #))
-
/// Generic comparison. Implements ER mode (where "0" is returned when NaNs are compared)
//
// The compiler optimizer is aware of this function (see use of generic_comparison_inner_vref in opt.fs)
// and devirtualizes calls to it based on "T".
let GenericComparisonIntrinsic<'T> (x:'T) (y:'T) : int =
- GenericComparisonWithComparerIntrinsic (fsComparerER :> IComparer) x y
-
+ FSharpComparer_ER.Comparer.Compare (x, y)
- /// Generic less-than. Uses comparison implementation in PER mode but catches
- /// the local exception that is thrown when NaN's are compared.
+ /// Generic less-than.
let GenericLessThanIntrinsic (x:'T) (y:'T) =
- try
- (# "clt" (GenericComparisonWithComparerIntrinsic fsComparerPER x y) 0 : bool #)
- with
- | e when System.Runtime.CompilerServices.RuntimeHelpers.Equals(e, NaNException) -> false
-
+ (# "clt" (FSharpComparer_ForLessThanComparison.Comparer.Compare (x, y)) 0 : bool #)
- /// Generic greater-than. Uses comparison implementation in PER mode but catches
- /// the local exception that is thrown when NaN's are compared.
+ /// Generic greater-than.
let GenericGreaterThanIntrinsic (x:'T) (y:'T) =
- try
- (# "cgt" (GenericComparisonWithComparerIntrinsic fsComparerPER x y) 0 : bool #)
- with
- | e when System.Runtime.CompilerServices.RuntimeHelpers.Equals(e, NaNException) -> false
-
+ (# "cgt" (FSharpComparer_ForGreaterThanComparison.Comparer.Compare (x, y)) 0 : bool #)
- /// Generic greater-than-or-equal. Uses comparison implementation in PER mode but catches
- /// the local exception that is thrown when NaN's are compared.
+ /// Generic greater-than-or-equal.
let GenericGreaterOrEqualIntrinsic (x:'T) (y:'T) =
- try
- (# "cgt" (GenericComparisonWithComparerIntrinsic fsComparerPER x y) (-1) : bool #)
- with
- | e when System.Runtime.CompilerServices.RuntimeHelpers.Equals(e, NaNException) -> false
-
+ (# "cgt" (FSharpComparer_ForGreaterThanComparison.Comparer.Compare (x, y)) -1 : bool #)
-
- /// Generic less-than-or-equal. Uses comparison implementation in PER mode but catches
- /// the local exception that is thrown when NaN's are compared.
+ /// Generic less-than-or-equal.
let GenericLessOrEqualIntrinsic (x:'T) (y:'T) =
- try
- (# "clt" (GenericComparisonWithComparerIntrinsic fsComparerPER x y) 1 : bool #)
- with
- | e when System.Runtime.CompilerServices.RuntimeHelpers.Equals(e, NaNException) -> false
-
+ (# "clt" (FSharpComparer_ForLessThanComparison.Comparer.Compare (x, y)) 1 : bool #)
/// Compare two values of the same generic type, in ER mode, with static optimizations
/// for known cases.
@@ -1247,105 +1946,30 @@ namespace Microsoft.FSharp.Core
when 'T : char = not (# "clt" x y : bool #)
when 'T : decimal = System.Decimal.op_GreaterThanOrEqual ((# "" x:decimal #), (# "" y:decimal #))
-
//-------------------------------------------------------------------------
// LanguagePrimitives.HashCompare: EQUALITY
//-------------------------------------------------------------------------
-
- /// optimized case: Core implementation of structural equality on arrays.
- let GenericEqualityByteArray (x:byte[]) (y:byte[]) : bool=
+ let inline ArrayEquality<'T> (f:'T->'T->bool) (x:'T[]) (y:'T[]) : bool =
let lenx = x.Length
let leny = y.Length
- let c = (lenx = leny)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = byteEq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural equality on arrays.
- let GenericEqualityInt32Array (x:int[]) (y:int[]) : bool=
- let lenx = x.Length
- let leny = y.Length
- let c = (lenx = leny)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = int32Eq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural equality on arrays
- let GenericEqualitySingleArray er (x:float32[]) (y:float32[]) : bool=
- let lenx = x.Length
- let leny = y.Length
- let f32eq x y = if er && not(float32Eq x x) && not(float32Eq y y) then true else (float32Eq x y)
- let c = (lenx = leny)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = f32eq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural equality on arrays.
- let GenericEqualityDoubleArray er (x:float[]) (y:float[]) : bool=
- let lenx = x.Length
- let leny = y.Length
- let c = (lenx = leny)
- let feq x y = if er && not(floatEq x x) && not(floatEq y y) then true else (floatEq x y)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = feq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural equality on arrays.
- let GenericEqualityCharArray (x:char[]) (y:char[]) : bool=
- let lenx = x.Length
- let leny = y.Length
- let c = (lenx = leny)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = charEq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
- /// optimized case: Core implementation of structural equality on arrays.
- let GenericEqualityInt64Array (x:int64[]) (y:int64[]) : bool=
- let lenx = x.Length
- let leny = y.Length
- let c = (lenx = leny)
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = int64Eq (get x i) (get y i)
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
-
+ let rec loop i =
+ if i = lenx then true
+ elif f (get x i) (get y i) then loop (i+1)
+ else false
+ (lenx = leny) && loop 0
+
+ let inline ArrayEqualityWithERFlag<'T> (er:bool) (f:'T->'T->bool) (x:'T[]) (y:'T[]) : bool =
+ if er
+ then ArrayEquality (fun x y -> (not (f x x) && not (f y y)) || (f x y)) x y
+ else ArrayEquality f x y
+
+ let GenericEqualityByteArray x y = ArrayEquality byteEq x y
+ let GenericEqualityInt32Array x y = ArrayEquality int32Eq x y
+ let GenericEqualitySingleArray er x y = ArrayEqualityWithERFlag er float32Eq x y
+ let GenericEqualityDoubleArray er x y = ArrayEqualityWithERFlag er floatEq x y
+ let GenericEqualityCharArray x y = ArrayEquality charEq x y
+ let GenericEqualityInt64Array x y = ArrayEquality int64Eq x y
/// The core implementation of generic equality between two objects. This corresponds
/// to th e pseudo-code in the F# language spec.
@@ -1353,8 +1977,8 @@ namespace Microsoft.FSharp.Core
// Run in either PER or ER mode. In PER mode, equality involving a NaN returns "false".
// In ER mode, equality on two NaNs returns "true".
//
- // If "er" is true the "iec" is fsEqualityComparerNoHashingER
- // If "er" is false the "iec" is fsEqualityComparerNoHashingPER
+ // If "er" is true the "iec" is fsEqualityComparerUnlimitedHashingER
+ // If "er" is false the "iec" is fsEqualityComparerUnlimitedHashingPER
let rec GenericEqualityObj (er:bool) (iec:System.Collections.IEqualityComparer) ((xobj:obj),(yobj:obj)) : bool =
(*if objEq xobj yobj then true else *)
match xobj,yobj with
@@ -1364,10 +1988,12 @@ namespace Microsoft.FSharp.Core
| (:? string as xs),(:? string as ys) -> System.String.Equals(xs,ys)
// Permit structural equality on arrays
| (:? System.Array as arr1),_ ->
+ // due to the rules of the CLI type system, array casts are "assignment compatible"
+ // see: https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent/
+ // this means that the cast and comparison for byte will also handle sbyte, int32 handle uint32,
+ // and int64 handle uint64. Equality will still be correct.
match arr1,yobj with
- // Fast path
| (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2
- // Fast path
| (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2
| (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2
| (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2
@@ -1503,41 +2129,154 @@ namespace Microsoft.FSharp.Core
#endif
/// optimized case: Core implementation of structural equality on object arrays.
- and GenericEqualityObjArray er iec (x:obj[]) (y:obj[]) : bool =
- let lenx = x.Length
- let leny = y.Length
- let c = (lenx = leny )
- if not c then c
- else
- let mutable i = 0
- let mutable res = true
- while i < lenx do
- let c = GenericEqualityObj er iec ((get x i),(get y i))
- if not c then (res <- false; i <- lenx)
- else i <- i + 1
- res
-
-
- /// One of the two unique instances of System.Collections.IEqualityComparer. Implements PER semantics
- /// where equality on NaN returns "false".
- let fsEqualityComparerNoHashingPER =
- { new System.Collections.IEqualityComparer with
- override iec.Equals(x:obj,y:obj) = GenericEqualityObj false iec (x,y) // PER Semantics
- override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) }
-
- /// One of the two unique instances of System.Collections.IEqualityComparer. Implements ER semantics
- /// where equality on NaN returns "true".
- let fsEqualityComparerNoHashingER =
- { new System.Collections.IEqualityComparer with
- override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y) // ER Semantics
- override iec.GetHashCode(x:obj) = raise (InvalidOperationException (SR.GetString(SR.notUsedForHashing))) }
+ and GenericEqualityObjArray er iec (xarray:obj[]) (yarray:obj[]) : bool =
+ ArrayEquality (fun x y -> GenericEqualityObj er iec (x, y)) xarray yarray
+
+ let isStructuralEquatable (ty:Type) = typeof.IsAssignableFrom ty
+ let isValueTypeStructuralEquatable (ty:Type) = isStructuralEquatable ty && ty.IsValueType
+
+ let canUseDefaultEqualityComparer er (rootType:Type) =
+ // "Default" equality for strings is by ordinal, so needs special handling required
+ canUseDotnetDefaultComparisonOrEquality Reflection.hasCustomEquality isStructuralEquatable false er rootType
+
+ let tryGetFSharpEqualityComparer (er:bool) (ty:Type) : obj =
+ match er, ty with
+ | false, ty when ty.Equals typeof ->
+ box { new EqualityComparer() with
+ member __.Equals (x,y) = (# "ceq" x y : bool #)
+ member __.GetHashCode x = x.GetHashCode () }
+ | false, ty when ty.Equals typeof ->
+ box { new EqualityComparer() with
+ member __.Equals (x,y) = (# "ceq" x y : bool #)
+ member __.GetHashCode x = x.GetHashCode () }
+ | true, ty when ty.Equals typeof -> box EqualityComparer.Default
+ | true, ty when ty.Equals typeof -> box EqualityComparer.Default
+ | _ -> null
+
+ let inline nullableEqualityComparer<'a when 'a : null> equals getHashCode =
+ box { new EqualityComparer<'a>() with
+ member __.Equals (x,y) =
+ match x, y with
+ | null, null -> true
+ | null, _ -> false
+ | _, null -> false
+ | _ -> equals x y
+
+ member __.GetHashCode x =
+ match x with
+ | null -> 0
+ | _ -> getHashCode x }
+
+ let inline castNullableEqualityComparer<'fromType, 'toType when 'toType : null and 'fromType : null> (equals:'toType->'toType->bool) (getHashCode:'toType->int) =
+ let castEquals (lhs:'fromType) (rhs:'fromType) = equals (unboxPrim lhs) (unboxPrim rhs)
+ let castGetHashCode (o:'fromType) = getHashCode (unboxPrim o)
+ nullableEqualityComparer castEquals castGetHashCode
+
+ let tryGetFSharpArrayEqualityComparer (ty:Type) er comparer : obj =
+ // the casts here between byte+sbyte, int32+uint32 and int64+uint64 are here to replicate the behaviour
+ // in GenericHashParamObj
+ if ty.Equals typeof then nullableEqualityComparer (fun x y -> GenericEqualityObjArray er comparer x y) (GenericHashObjArray fsEqualityComparerUnlimitedHashingPER)
+ elif ty.Equals typeof then nullableEqualityComparer GenericEqualityByteArray GenericHashByteArray
+ elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityByteArray GenericHashByteArray
+ elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array
+ elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityInt32Array GenericHashInt32Array
+ elif ty.Equals typeof then nullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array
+ elif ty.Equals typeof then castNullableEqualityComparer GenericEqualityInt64Array GenericHashInt64Array
+ else null
+
+ let arrayEqualityComparer<'T> er comparer =
+ match tryGetFSharpArrayEqualityComparer typeof<'T> er comparer with
+ | :? EqualityComparer<'T> as arrayComparer -> arrayComparer
+ | _ ->
+ { new EqualityComparer<'T>() with
+ member __.Equals (x, y) =
+ let xobj, yobj = box x, box y
+ match xobj,yobj with
+ | null, null -> true
+ | null, _ -> false
+ | _, null -> false
+ | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2
+ | _ ->
+ match xobj,yobj with
+ | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2
+ | _ ->
+ match xobj,yobj with
+ | (:? (float[]) as arr1), (:? (float[])as arr2) -> GenericEqualityDoubleArray er arr1 arr2
+ | _ ->
+ match xobj,yobj with
+ | (:? System.Array as arr1), (:? System.Array as arr2) -> GenericEqualityArbArray er comparer arr1 arr2
+ | _ -> raise (Exception "invalid logic - expected array")
+
+ member __.GetHashCode x =
+ match box x with
+ | null -> 0
+ | :? System.Array as a -> GenericHashArbArray fsEqualityComparerUnlimitedHashingPER a
+ | _ -> raise (Exception "invalid logic - expected array") }
+
+ let structuralEqualityComparer<'T> comparer =
+ { new EqualityComparer<'T>() with
+ member __.Equals (x,y) =
+ match box x, box y with
+ | null, null -> true
+ | null, _ -> false
+ | _, null -> false
+ | (:? IStructuralEquatable as x1), yobj -> x1.Equals (yobj, comparer)
+ | _ -> raise (Exception "invalid logic - expected IStructuralEquatable")
+
+ member __.GetHashCode x =
+ match box x with
+ | null -> 0
+ | :? IStructuralEquatable as a -> a.GetHashCode fsEqualityComparerUnlimitedHashingPER
+ | _ -> raise (Exception "invalid logic - expected IStructuralEquatable") }
+
+ let structuralEqualityComparerValueType<'T> comparer =
+ { new EqualityComparer<'T>() with
+ member __.Equals (x,y) = ((box x):?>IStructuralEquatable).Equals (y, comparer)
+ member __.GetHashCode x = ((box x):?>IStructuralEquatable).GetHashCode fsEqualityComparerUnlimitedHashingPER }
+
+ let unknownEqualityComparer<'T> er comparer =
+ { new EqualityComparer<'T>() with
+ member __.Equals (x,y) = GenericEqualityObj er comparer (box x, box y)
+ member __.GetHashCode x = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box x) }
+
+ let getGenericEquality<'T> er =
+ match tryGetFSharpEqualityComparer er typeof<'T> with
+ | :? EqualityComparer<'T> as call -> call
+ | _ when canUseDefaultEqualityComparer er typeof<'T> -> EqualityComparer<'T>.Default
+ | _ when isArray typeof<'T> && er -> arrayEqualityComparer true fsEqualityComparerUnlimitedHashingER
+ | _ when isArray typeof<'T> -> arrayEqualityComparer false fsEqualityComparerUnlimitedHashingPER
+ | _ when isValueTypeStructuralEquatable typeof<'T> && er -> structuralEqualityComparerValueType fsEqualityComparerUnlimitedHashingER
+ | _ when isValueTypeStructuralEquatable typeof<'T> -> structuralEqualityComparerValueType fsEqualityComparerUnlimitedHashingPER
+ | _ when isStructuralEquatable typeof<'T> && er -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingER
+ | _ when isStructuralEquatable typeof<'T> -> structuralEqualityComparer fsEqualityComparerUnlimitedHashingPER
+ | _ when er -> unknownEqualityComparer true fsEqualityComparerUnlimitedHashingER
+ | _ -> unknownEqualityComparer false fsEqualityComparerUnlimitedHashingPER
+
+ []
+ type FSharpEqualityComparer_ER<'T> private () =
+ static let comparer = getGenericEquality<'T> true
+ static member EqualityComparer = comparer
+
+ []
+ type FSharpEqualityComparer_PER<'T> private () =
+ static let comparer = getGenericEquality<'T> false
+ static member EqualityComparer = comparer
+
+ let inline FSharpEqualityComparer_ER_Equals (x:'T) (y:'T) =
+ FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y)
+
+ let inline FSharpEqualityComparer_PER_Equals (x:'T) (y:'T) =
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y)
+
+ let inline FSharpEqualityComparer_GetHashCode (x:'T) =
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode x
/// Implements generic equality between two values, with PER semantics for NaN (so equality on two NaN values returns false)
//
// The compiler optimizer is aware of this function (see use of generic_equality_per_inner_vref in opt.fs)
// and devirtualizes calls to it based on "T".
let GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool =
- GenericEqualityObj false fsEqualityComparerNoHashingPER ((box x), (box y))
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y)
/// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true)
//
@@ -1546,16 +2285,20 @@ namespace Microsoft.FSharp.Core
// The compiler optimizer is aware of this function (see use of generic_equality_er_inner_vref in opt.fs)
// and devirtualizes calls to it based on "T".
let GenericEqualityERIntrinsic (x : 'T) (y : 'T) : bool =
- GenericEqualityObj true fsEqualityComparerNoHashingER ((box x), (box y))
+ FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y)
/// Implements generic equality between two values using "comp" for recursive calls.
//
// The compiler optimizer is aware of this function (see use of generic_equality_withc_inner_vref in opt.fs)
// and devirtualizes calls to it based on "T", and under the assumption that "comp"
- // is either fsEqualityComparerNoHashingER or fsEqualityComparerNoHashingPER.
+ // is either fsEqualityComparerUnlimitedHashingER or fsEqualityComparerUnlimitedHashingPER.
let GenericEqualityWithComparerIntrinsic (comp : System.Collections.IEqualityComparer) (x : 'T) (y : 'T) : bool =
- comp.Equals((box x),(box y))
-
+ if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals (x, y)
+ elif obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingER) then
+ FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals (x, y)
+ else
+ comp.Equals (box x, box y)
/// Implements generic equality between two values, with ER semantics for NaN (so equality on two NaN values returns true)
//
@@ -1641,127 +2384,6 @@ namespace Microsoft.FSharp.Core
let inline GenericInequalityFast (x:'T) (y:'T) = (not(GenericEqualityFast x y) : bool)
let inline GenericInequalityERFast (x:'T) (y:'T) = (not(GenericEqualityERFast x y) : bool)
-
- //-------------------------------------------------------------------------
- // LanguagePrimitives.HashCompare: HASHING.
- //-------------------------------------------------------------------------
-
-
-
- let defaultHashNodes = 18
-
- /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality.
- type CountLimitedHasherPER(sz:int) =
- []
- val mutable nodeCount : int
-
- member x.Fresh() =
- if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then
- x
- else
- new CountLimitedHasherPER(sz)
-
- interface IEqualityComparer
-
- /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality.
- type UnlimitedHasherER() =
- interface IEqualityComparer
-
- /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality.
- type UnlimitedHasherPER() =
- interface IEqualityComparer
-
-
- /// The unique object for unlimited depth for hashing and ER semantics for equality.
- let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER()
-
- /// The unique object for unlimited depth for hashing and PER semantics for equality.
- let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER()
-
- let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr
-
- let GenericHashObjArray (iec : System.Collections.IEqualityComparer) (x: obj[]) : int =
- let len = x.Length
- let mutable i = len - 1
- if i > defaultHashNodes then i <- defaultHashNodes // limit the hash
- let mutable acc = 0
- while (i >= 0) do
- // NOTE: GenericHash* call decreases nr
- acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i)));
- i <- i - 1
- acc
-
- // optimized case - byte arrays
- let GenericHashByteArray (x: byte[]) : int =
- let len = length x
- let mutable i = len - 1
- if i > defaultHashNodes then i <- defaultHashNodes // limit the hash
- let mutable acc = 0
- while (i >= 0) do
- acc <- HashCombine i acc (intOfByte (get x i));
- i <- i - 1
- acc
-
- // optimized case - int arrays
- let GenericHashInt32Array (x: int[]) : int =
- let len = length x
- let mutable i = len - 1
- if i > defaultHashNodes then i <- defaultHashNodes // limit the hash
- let mutable acc = 0
- while (i >= 0) do
- acc <- HashCombine i acc (get x i);
- i <- i - 1
- acc
-
- // optimized case - int arrays
- let GenericHashInt64Array (x: int64[]) : int =
- let len = length x
- let mutable i = len - 1
- if i > defaultHashNodes then i <- defaultHashNodes // limit the hash
- let mutable acc = 0
- while (i >= 0) do
- acc <- HashCombine i acc (int32 (get x i));
- i <- i - 1
- acc
-
- // special case - arrays do not by default have a decent structural hashing function
- let GenericHashArbArray (iec : System.Collections.IEqualityComparer) (x: System.Array) : int =
- match x.Rank with
- | 1 ->
- let b = x.GetLowerBound(0)
- let len = x.Length
- let mutable i = b + len - 1
- if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash
- let mutable acc = 0
- while (i >= b) do
- // NOTE: GenericHash* call decreases nr
- acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i)));
- i <- i - 1
- acc
- | _ ->
- HashCombine 10 (x.GetLength(0)) (x.GetLength(1))
-
- // Core implementation of structural hashing, corresponds to pseudo-code in the
- // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode().
- // Arrays are structurally hashed through a separate technique.
- //
- // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER.
- let rec GenericHashParamObj (iec : System.Collections.IEqualityComparer) (x: obj) : int =
- match x with
- | null -> 0
- | (:? System.Array as a) ->
- match a with
- | :? (obj[]) as oa -> GenericHashObjArray iec oa
- | :? (byte[]) as ba -> GenericHashByteArray ba
- | :? (int[]) as ba -> GenericHashInt32Array ba
- | :? (int64[]) as ba -> GenericHashInt64Array ba
- | _ -> GenericHashArbArray iec a
- | :? IStructuralEquatable as a ->
- a.GetHashCode(iec)
- | _ ->
- x.GetHashCode()
-
-
/// Fill in the implementation of CountLimitedHasherPER
type CountLimitedHasherPER with
@@ -1777,7 +2399,6 @@ namespace Microsoft.FSharp.Core
/// Fill in the implementation of UnlimitedHasherER
type UnlimitedHasherER with
-
interface System.Collections.IEqualityComparer with
override iec.Equals(x:obj,y:obj) = GenericEqualityObj true iec (x,y)
override iec.GetHashCode(x:obj) = GenericHashParamObj iec x
@@ -1792,10 +2413,12 @@ namespace Microsoft.FSharp.Core
//
// NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_inner_vref in opt.fs)
// and devirtualizes calls to it based on type "T".
- let GenericHashIntrinsic input = GenericHashParamObj fsEqualityComparerUnlimitedHashingPER (box input)
+ let GenericHashIntrinsic input =
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode input
/// Intrinsic for calls to depth-limited structural hashing that were not optimized by static conditionals.
- let LimitedGenericHashIntrinsic limit input = GenericHashParamObj (CountLimitedHasherPER(limit)) (box input)
+ let LimitedGenericHashIntrinsic limit input =
+ GenericHashParamObj (CountLimitedHasherPER(limit)) (box input)
/// Intrinsic for a recursive call to structural hashing that was not optimized by static conditionals.
//
@@ -1805,23 +2428,11 @@ namespace Microsoft.FSharp.Core
// NOTE: The compiler optimizer is aware of this function (see uses of generic_hash_withc_inner_vref in opt.fs)
// and devirtualizes calls to it based on type "T".
let GenericHashWithComparerIntrinsic<'T> (comp : System.Collections.IEqualityComparer) (input : 'T) : int =
- GenericHashParamObj comp (box input)
+ if obj.ReferenceEquals (comp, fsEqualityComparerUnlimitedHashingPER) then
+ FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode input
+ else
+ GenericHashParamObj comp (box input)
- /// Direct call to GetHashCode on the string type
- let inline HashString (s:string) =
- match s with
- | null -> 0
- | _ -> (# "call instance int32 [mscorlib]System.String::GetHashCode()" s : int #)
-
- // from mscorlib v4.0.30319
- let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #)
- let inline HashSByte (x:sbyte) = (# "xor" (# "shl" x 8 : int #) x : int #)
- let inline HashInt16 (x:int16) = (# "or" (# "conv.u2" x : int #) (# "shl" x 16 : int #) : int #)
- let inline HashInt64 (x:int64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr" x 32 : int #) : int #) : int #)
- let inline HashUInt64 (x:uint64) = (# "xor" (# "conv.i4" x : int #) (# "conv.i4" (# "shr.un" x 32 : int #) : int #) : int #)
- let inline HashIntPtr (x:nativeint) = (# "conv.i4" (# "conv.u8" x : uint64 #) : int #)
- let inline HashUIntPtr (x:unativeint) = (# "and" (# "conv.i4" (# "conv.u8" x : uint64 #) : int #) 0x7fffffff : int #)
-
/// Core entry into structural hashing for either limited or unlimited hashing.
//
// "iec" is assumed to be either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or
@@ -2082,7 +2693,6 @@ namespace Microsoft.FSharp.Core
// LanguagePrimitives: PUBLISH IEqualityComparer AND IComparer OBJECTS
//-------------------------------------------------------------------------
-
let inline MakeGenericEqualityComparer<'T>() =
// type-specialize some common cases to generate more efficient functions
{ new System.Collections.Generic.IEqualityComparer<'T> with
@@ -2095,47 +2705,8 @@ namespace Microsoft.FSharp.Core
member self.GetHashCode(x) = GenericLimitedHash limit x
member self.Equals(x,y) = GenericEquality x y }
- let BoolIEquality = MakeGenericEqualityComparer()
- let CharIEquality = MakeGenericEqualityComparer()
- let StringIEquality = MakeGenericEqualityComparer()
- let SByteIEquality = MakeGenericEqualityComparer()
- let Int16IEquality = MakeGenericEqualityComparer()
- let Int32IEquality = MakeGenericEqualityComparer()
- let Int64IEquality = MakeGenericEqualityComparer()
- let IntPtrIEquality = MakeGenericEqualityComparer()
- let ByteIEquality = MakeGenericEqualityComparer()
- let UInt16IEquality = MakeGenericEqualityComparer()
- let UInt32IEquality = MakeGenericEqualityComparer()
- let UInt64IEquality = MakeGenericEqualityComparer()
- let UIntPtrIEquality = MakeGenericEqualityComparer()
- let FloatIEquality = MakeGenericEqualityComparer()
- let Float32IEquality = MakeGenericEqualityComparer()
- let DecimalIEquality = MakeGenericEqualityComparer()
-
- []
- type FastGenericEqualityComparerTable<'T>() =
- static let f : System.Collections.Generic.IEqualityComparer<'T> =
- match typeof<'T> with
- | ty when ty.Equals(typeof) -> unboxPrim (box BoolIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box ByteIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box Int32IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box UInt32IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box CharIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box SByteIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box Int16IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box Int64IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box IntPtrIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box UInt16IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box UInt64IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box UIntPtrIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box FloatIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box Float32IEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box DecimalIEquality)
- | ty when ty.Equals(typeof) -> unboxPrim (box StringIEquality)
- | _ -> MakeGenericEqualityComparer<'T>()
- static member Function : System.Collections.Generic.IEqualityComparer<'T> = f
-
- let FastGenericEqualityComparerFromTable<'T> = FastGenericEqualityComparerTable<'T>.Function
+ let FastGenericEqualityComparerFromTable<'T> =
+ HashCompare.FSharpEqualityComparer_PER<'T>.EqualityComparer :> IEqualityComparer<'T>
// This is the implementation of HashIdentity.Structural. In most cases this just becomes
// FastGenericEqualityComparerFromTable.
@@ -2173,93 +2744,9 @@ namespace Microsoft.FSharp.Core
let inline MakeGenericComparer<'T>() =
{ new System.Collections.Generic.IComparer<'T> with
member __.Compare(x,y) = GenericComparison x y }
-
- let CharComparer = MakeGenericComparer()
- let StringComparer = MakeGenericComparer()
- let SByteComparer = MakeGenericComparer()
- let Int16Comparer = MakeGenericComparer()
- let Int32Comparer = MakeGenericComparer()
- let Int64Comparer = MakeGenericComparer()
- let IntPtrComparer = MakeGenericComparer()
- let ByteComparer = MakeGenericComparer()
- let UInt16Comparer = MakeGenericComparer()
- let UInt32Comparer = MakeGenericComparer()
- let UInt64Comparer = MakeGenericComparer()
- let UIntPtrComparer = MakeGenericComparer()
- let FloatComparer = MakeGenericComparer()
- let Float32Comparer = MakeGenericComparer()
- let DecimalComparer = MakeGenericComparer()
- let BoolComparer = MakeGenericComparer()
-
- /// Use a type-indexed table to ensure we only create a single FastStructuralComparison function
- /// for each type
- []
- type FastGenericComparerTable<'T>() =
-
- // The CLI implementation of mscorlib optimizes array sorting
- // when the comparer is either null or precisely
- // reference-equals to System.Collections.Generic.Comparer<'T>.Default.
- // This is an indication that a "fast" array sorting helper can be used.
- //
- // So, for all the types listed below, we want to pass in a value of "null" for
- // the comparer object. Note that F# generic comparison coincides precisely with
- // System.Collections.Generic.Comparer<'T>.Default for these types.
- //
- // A "null" comparer is only valid if the values do not have identity, e.g. integers.
- // That is, an unstable sort of the array must be the semantically the
- // same as a stable sort of the array. See Array.stableSortInPlace.
- //
- // REVIEW: in a future version we could extend this to include additional types
- static let fCanBeNull : System.Collections.Generic.IComparer<'T> =
- match typeof<'T> with
- | ty when ty.Equals(typeof) -> unboxPrim (box IntPtrComparer)
- | ty when ty.Equals(typeof) -> unboxPrim (box UIntPtrComparer)
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof) -> null
- | ty when ty.Equals(typeof