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/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) -> null - | ty when ty.Equals(typeof) -> null - | ty when ty.Equals(typeof) -> null - | ty when ty.Equals(typeof) -> unboxPrim (box StringComparer) - | ty when ty.Equals(typeof) -> null - | _ -> MakeGenericComparer<'T>() - - static let f : System.Collections.Generic.IComparer<'T> = - match typeof<'T> with - | ty when ty.Equals(typeof) -> unboxPrim (box ByteComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box CharComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box SByteComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box Int16Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box Int32Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box Int64Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box IntPtrComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt16Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt32Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box UInt64Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box UIntPtrComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box FloatComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box Float32Comparer) - | ty when ty.Equals(typeof) -> unboxPrim (box DecimalComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box StringComparer) - | ty when ty.Equals(typeof) -> unboxPrim (box BoolComparer) - | _ -> - // Review: There are situations where we should be able - // to return System.Collections.Generic.Comparer<'T>.Default here. - // For example, for any value type. - MakeGenericComparer<'T>() - - static member Value : System.Collections.Generic.IComparer<'T> = f - - static member ValueCanBeNullIfDefaultSemantics : System.Collections.Generic.IComparer<'T> = fCanBeNull - let FastGenericComparerFromTable<'T> = - FastGenericComparerTable<'T>.Value + let FastGenericComparerFromTable<'T> : IComparer<'T> = + HashCompare.FSharpComparer_ER<'T>.Comparer :> IComparer<'T> let inline FastGenericComparer<'T> = // This gets used is 'T can't be resolved to anything interesting @@ -2290,7 +2777,10 @@ namespace Microsoft.FSharp.Core // which are then optimized for the particular nominal type involved. when 'T : 'T = MakeGenericComparer<'T>() - let FastGenericComparerCanBeNull<'T> = FastGenericComparerTable<'T>.ValueCanBeNullIfDefaultSemantics + let FastGenericComparerInternal<'T> : Comparer<'T> = + HashCompare.FSharpComparer_InternalUse_ER<'T>.Comparer + let EquivalentForStableAndUnstableSort<'T> : bool = + HashCompare.FSharpComparer_InternalUse_ER<'T>.EquivalentForStableAndUnstableSort //------------------------------------------------------------------------- // LanguagePrimitives: ENUMS diff --git a/src/fsharp/FSharp.Core/prim-types.fsi b/src/fsharp/FSharp.Core/prim-types.fsi index 54fdf39ba2c..c233f80d23f 100644 --- a/src/fsharp/FSharp.Core/prim-types.fsi +++ b/src/fsharp/FSharp.Core/prim-types.fsi @@ -952,7 +952,10 @@ namespace Microsoft.FSharp.Core val inline FastGenericComparer<'T> : System.Collections.Generic.IComparer<'T> when 'T : comparison /// Make an F# comparer object for the given type, where it can be null if System.Collections.Generic.Comparer<'T>.Default - val internal FastGenericComparerCanBeNull<'T> : System.Collections.Generic.IComparer<'T> when 'T : comparison + val internal FastGenericComparerInternal<'T> : System.Collections.Generic.Comparer<'T> when 'T : comparison + + /// As an optimization, determine if a fast unstable sort can be used with equivalent results + val internal EquivalentForStableAndUnstableSort<'T> : bool /// Make an F# hash/equality object for the given type val inline FastGenericEqualityComparer<'T> : System.Collections.Generic.IEqualityComparer<'T> when 'T : equality @@ -1236,8 +1239,38 @@ namespace Microsoft.FSharp.Core //[] val inline SetArray4D : target:'T[,,,] -> index1:int -> index2:int -> index3:int -> index4:int -> value:'T -> unit + module internal Reflection = + val internal tupleNames : string [] + val internal isTupleType : Type -> bool + val internal tryFindSourceConstructFlagsOfType : Type * byref -> bool + val internal fieldPropsOfRecordType : Type * System.Reflection.BindingFlags -> System.Reflection.PropertyInfo[] + val internal isRecordType : Type * System.Reflection.BindingFlags -> bool + val internal getUnionTypeTagNameMap : Type * System.Reflection.BindingFlags -> (int*string)[] + val internal fieldsPropsOfUnionCase : Type * int* System.Reflection.BindingFlags -> System.Reflection.PropertyInfo[] + val internal isUnionType : Type * System.Reflection.BindingFlags -> bool + /// The F# compiler emits calls to some of the functions in this module as part of the compiled form of some language constructs module HashCompare = + [] + type FSharpEqualityComparer_ER<'T> = + static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T> + + [] + type FSharpEqualityComparer_PER<'T> = + static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T> + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_ER_Equals : x:'T -> y:'T -> bool + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_PER_Equals : x:'T -> y:'T -> bool + + /// A primitive entry point used by the F# compiler for optimization purposes. + [] + val inline FSharpEqualityComparer_GetHashCode : x:'T -> int + /// A primitive entry point used by the F# compiler for optimization purposes. [] val PhysicalHashIntrinsic : input:'T -> int when 'T : not struct diff --git a/src/fsharp/FSharp.Core/reflect.fs b/src/fsharp/FSharp.Core/reflect.fs index 78528294f5c..ef019303f61 100644 --- a/src/fsharp/FSharp.Core/reflect.fs +++ b/src/fsharp/FSharp.Core/reflect.fs @@ -83,153 +83,17 @@ module internal Impl = //----------------------------------------------------------------- // ATTRIBUTE DECOMPILATION - let tryFindCompilationMappingAttribute (attrs:obj[]) = - match attrs with - | null | [| |] -> None - | [| res |] -> let a = (res :?> CompilationMappingAttribute) in Some (a.SourceConstructFlags, a.SequenceNumber, a.VariantNumber) - | _ -> raise <| System.InvalidOperationException (SR.GetString(SR.multipleCompilationMappings)) - - let findCompilationMappingAttribute (attrs:obj[]) = - match tryFindCompilationMappingAttribute attrs with - | None -> failwith "no compilation mapping attribute" - | Some a -> a - -#if !FX_NO_REFLECTION_ONLY - let cmaName = typeof.FullName - let assemblyName = typeof.Assembly.GetName().Name - let _ = assert (assemblyName = "FSharp.Core") - - let tryFindCompilationMappingAttributeFromData (attrs:System.Collections.Generic.IList) = - match attrs with - | null -> None - | _ -> - let mutable res = None - for a in attrs do - if a.Constructor.DeclaringType.FullName = 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)) - | _ -> (enum 0, 0, 0) - res <- Some flags - res - - let findCompilationMappingAttributeFromData attrs = - match tryFindCompilationMappingAttributeFromData attrs with - | None -> failwith "no compilation mapping attribute" - | Some a -> a -#endif - - let tryFindCompilationMappingAttributeFromType (typ:Type) = -#if !FX_NO_REFLECTION_ONLY - let assem = typ.Assembly - if (not (isNull assem)) && assem.ReflectionOnly then - tryFindCompilationMappingAttributeFromData ( typ.GetCustomAttributesData()) - else -#endif - tryFindCompilationMappingAttribute ( typ.GetCustomAttributes (typeof,false)) - - let tryFindCompilationMappingAttributeFromMemberInfo (info:MemberInfo) = -#if !FX_NO_REFLECTION_ONLY - let assem = info.DeclaringType.Assembly - if (not (isNull assem)) && assem.ReflectionOnly then - tryFindCompilationMappingAttributeFromData (info.GetCustomAttributesData()) - else -#endif - tryFindCompilationMappingAttribute (info.GetCustomAttributes (typeof,false)) - - let findCompilationMappingAttributeFromMemberInfo (info:MemberInfo) = -#if !FX_NO_REFLECTION_ONLY - let assem = info.DeclaringType.Assembly - if (not (isNull assem)) && assem.ReflectionOnly then - findCompilationMappingAttributeFromData (info.GetCustomAttributesData()) + let tryFindSourceConstructFlagsOfType (typ:Type) = + let mutable res = Unchecked.defaultof<_> + if LanguagePrimitives.Reflection.tryFindSourceConstructFlagsOfType (typ, &res) then + Some res else -#endif - findCompilationMappingAttribute (info.GetCustomAttributes (typeof,false)) - - let sequenceNumberOfMember (x: MemberInfo) = let (_,n,_) = findCompilationMappingAttributeFromMemberInfo x in n - let variantNumberOfMember (x: MemberInfo) = let (_,_,vn) = findCompilationMappingAttributeFromMemberInfo x in vn - - let sortFreshArray f arr = Array.sortInPlaceWith f arr; arr - - let isFieldProperty (prop : PropertyInfo) = - match tryFindCompilationMappingAttributeFromMemberInfo(prop) with - | None -> false - | Some (flags,_n,_vn) -> (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.Field - - let tryFindSourceConstructFlagsOfType (typ:Type) = - match tryFindCompilationMappingAttributeFromType typ with - | None -> None - | Some (flags,_n,_vn) -> Some flags + None //----------------------------------------------------------------- // UNION DECOMPILATION - // Get the type where the type definitions are stored - 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 getUnionTypeTagNameMap (typ:Type,bindingFlags) = - 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 -> - typ.GetMethods(staticMethodFlags ||| bindingFlags) - |> Array.choose (fun minfo -> - match tryFindCompilationMappingAttributeFromMemberInfo(minfo) with - | None -> None - | Some (flags,n,_vn) -> - if (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.UnionCase then - let nm = minfo.Name - // chop "get_" or "New" off the front - let nm = - if not (isListType typ) && not (isOptionType typ) then - if nm.Length > 4 && nm.[0..3] = "get_" then nm.[4..] - elif nm.Length > 3 && nm.[0..2] = "New" then nm.[3..] - else nm - else nm - Some (n, nm) - else - None) - | _ -> - enumTyp.GetFields(staticFieldFlags ||| bindingFlags) - |> Array.filter (fun (f:FieldInfo) -> f.IsStatic && f.IsLiteral) - |> sortFreshArray (fun f1 f2 -> compare (f1.GetValue(null) :?> int) (f2.GetValue(null) :?> int)) - |> Array.map (fun tagfield -> (tagfield.GetValue(null) :?> int),tagfield.Name) - - let getUnionCaseTyp (typ: Type, tag: int, bindingFlags) = - let tagFields = getUnionTypeTagNameMap(typ,bindingFlags) - let tagField = tagFields |> Array.pick (fun (i,f) -> if i = tag then Some f else None) - 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|] -> - (attr.Flags &&& CompilationRepresentationFlags.UseNullAsTrueValue) = 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 getUnionTypeTagNameMap (typ:Type,bindingFlags) = LanguagePrimitives.Reflection.getUnionTypeTagNameMap (typ, bindingFlags) let getUnionTagConverter (typ:Type,bindingFlags) = if isOptionType typ then (fun tag -> match tag with 0 -> "None" | 1 -> "Some" | _ -> invalidArg "tag" (SR.GetString(SR.outOfRange))) @@ -239,17 +103,11 @@ module internal Impl = (fun tag -> tagfieldmap.[tag]) let isUnionType (typ:Type,bindingFlags:BindingFlags) = + // isOptionType & isListType are not necessary. There were here before the code was refactored into prim-types + // presumably as an optimization, so have not been removed (no performance testing run at this time) isOptionType typ || - isListType typ || - match tryFindSourceConstructFlagsOfType(typ) with - | None -> false - | Some(flags) -> - (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.SumType && - // We see private representations only if BindingFlags.NonPublic is set - (if (flags &&& SourceConstructFlags.NonPublicRepresentation) <> enum(0) then - (bindingFlags &&& BindingFlags.NonPublic) <> enum(0) - else - true) + isListType typ || + LanguagePrimitives.Reflection.isUnionType (typ, bindingFlags) // Check the base type - if it is also an F# type then // for the moment we know it is a Discriminated Union @@ -273,14 +131,7 @@ module internal Impl = | 1 (* Cons *) -> getInstancePropertyInfos (typ,[| "Head"; "Tail" |],bindingFlags) | _ -> failwith "fieldsPropsOfUnionCase" else - // Lookup the type holding the fields for the union case - let caseTyp = getUnionCaseTyp (typ, tag, bindingFlags) - let caseTyp = match caseTyp with null -> typ | _ -> caseTyp - caseTyp.GetProperties(instancePropertyFlags ||| bindingFlags) - |> Array.filter isFieldProperty - |> Array.filter (fun prop -> variantNumberOfMember prop = tag) - |> sortFreshArray (fun p1 p2 -> compare (sequenceNumberOfMember p1) (sequenceNumberOfMember p2)) - + LanguagePrimitives.Reflection.fieldsPropsOfUnionCase (typ, tag, bindingFlags) let getUnionCaseRecordReader (typ:Type,tag:int,bindingFlags) = let props = fieldsPropsOfUnionCase(typ,tag,bindingFlags) @@ -349,32 +200,9 @@ module internal Impl = //----------------------------------------------------------------- // TUPLE DECOMPILATION - 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 && - typ.Namespace = "System" && - simpleTupleNames |> Seq.exists typ.Name.StartsWith + let tupleNames = LanguagePrimitives.Reflection.tupleNames + + let isTupleType (typ:Type) = LanguagePrimitives.Reflection.isTupleType typ let maxTuple = 8 // Which field holds the nested tuple? @@ -598,21 +426,9 @@ module internal Impl = //----------------------------------------------------------------- // RECORD DECOMPILATION - let isRecordType (typ:Type,bindingFlags:BindingFlags) = - match tryFindSourceConstructFlagsOfType(typ) with - | None -> false - | Some(flags) -> - (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.RecordType && - // We see private representations only if BindingFlags.NonPublic is set - (if (flags &&& SourceConstructFlags.NonPublicRepresentation) <> enum(0) then - (bindingFlags &&& BindingFlags.NonPublic) <> enum(0) - else - true) - - let fieldPropsOfRecordType(typ:Type,bindingFlags) = - typ.GetProperties(instancePropertyFlags ||| bindingFlags) - |> Array.filter isFieldProperty - |> sortFreshArray (fun p1 p2 -> compare (sequenceNumberOfMember p1) (sequenceNumberOfMember p2)) + let isRecordType (typ:Type,bindingFlags:BindingFlags) = LanguagePrimitives.Reflection.isRecordType (typ, bindingFlags) + + let fieldPropsOfRecordType (typ:Type, bindingFlags) = LanguagePrimitives.Reflection.fieldPropsOfRecordType (typ, bindingFlags) let getRecordReader(typ:Type,bindingFlags) = let props = fieldPropsOfRecordType(typ,bindingFlags) diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index da19114bcc7..d5bdf9a282d 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -2328,12 +2328,15 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = // REVIEW: GenericEqualityIntrinsic (which has no comparer) implements PER semantics (5537: this should be ER semantics) // We are devirtualizing to a Equals(T) method which also implements PER semantics (5537: this should be ER semantics) | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_er_inner_vref ty args -> - + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsValues with | Some (_, vref) -> Some (DevirtualizeApplication cenv env vref ty tyargs args m) - | _ -> None - + | _ -> + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals" + match cenv.g.fsharpEqualityComparer_ER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparerFast | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_withc_inner_vref ty args -> @@ -2345,23 +2348,33 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = Some (DevirtualizeApplication cenv env withcEqualsVal ty tyargs args2 m) | _ -> None - // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityWithComparer + // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericEqualityIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_equality_per_inner_vref ty args && not(isRefTupleTy cenv.g ty) -> + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsWithComparerValues, args with | Some (_, _, withcEqualsVal), [x; y] -> let args2 = [x; mkRefTupledNoTypes cenv.g m [mkCoerceExpr(y, cenv.g.obj_ty, m, ty); (mkCallGetGenericPEREqualityComparer cenv.g m)]] Some (DevirtualizeApplication cenv env withcEqualsVal ty tyargs args2 m) - | _ -> None + | _ -> + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals" + match cenv.g.fsharpEqualityComparer_PER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_inner_vref ty args -> + let tyargsOriginal = tyargs let tcref, tyargs = StripToNominalTyconRef cenv ty match tcref.GeneratedHashAndEqualsWithComparerValues, args with | Some (_, withcGetHashCodeVal, _), [x] -> let args2 = [x; mkCallGetGenericEREqualityComparer cenv.g m] Some (DevirtualizeApplication cenv env withcGetHashCodeVal ty tyargs args2 m) - | _ -> None + | _ -> + // if type of generic argument has no generated equality operators, covert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode" + match cenv.g.fsharpEqualityComparer_GetHashCode_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargsOriginal args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericHashWithComparerIntrinsic | Expr.Val(v, _, _), [ty], _ when CanDevirtualizeApplication cenv v cenv.g.generic_hash_withc_inner_vref ty args -> @@ -2416,6 +2429,24 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) = match vref with | Some vref -> Some (DevirtualizeApplication cenv env vref ty tyargs (mkCallGetGenericPEREqualityComparer cenv.g m :: args) m) | None -> None + + // "GenericEqualityIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.Equals" + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_per_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + match cenv.g.fsharpEqualityComparer_PER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_PER_Equals_vref ty tyargs args m) + + // "GenericEqualityERIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_ER<'T>.EqualityComparer.Equals" + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_equality_er_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + match cenv.g.fsharpEqualityComparer_ER_Equals_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_ER_Equals_vref ty tyargs args m) + + // "GenericHashIntrinsic" when found in a generic context, convert to "FSharpEqualityComparer_PER<'T>.EqualityComparer.GetHashCode" + | Expr.Val(v, _, _), [(TType_var t) as ty], _ when (not cenv.g.compilingFslib) && valRefEq cenv.g v cenv.g.generic_hash_inner_vref && t.Rigidity = TyparRigidity.Rigid -> + match cenv.g.fsharpEqualityComparer_GetHashCode_vref.TryDeref with + | ValueNone -> None // referencing old version of FSharp.Core.dll + | _ -> Some (DevirtualizeApplication cenv env cenv.g.fsharpEqualityComparer_GetHashCode_vref ty tyargs args m) // Optimize/analyze calls to LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic for tuple types | Expr.Val(v, _, _), [ty], _ when valRefEq cenv.g v cenv.g.generic_comparison_withc_inner_vref && isRefTupleTy cenv.g ty -> diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index d6fb7cd3c73..70ff2912bfb 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -583,6 +583,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_generic_comparison_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericComparisonIntrinsic" , None , None , [vara], mk_compare_sig varaTy) let v_generic_comparison_withc_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericComparisonWithComparerIntrinsic", None , None , [vara], mk_compare_withc_sig varaTy) + let v_FSharpEqualityComparer_PER_Equals_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_PER_Equals" , None , None , [vara], mk_rel_sig varaTy) + let v_FSharpEqualityComparer_GetHashCode_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_GetHashCode", None , None , [vara], mk_hash_sig varaTy) + let v_FSharpEqualityComparer_ER_Equals_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "FSharpEqualityComparer_ER_Equals" , None , None , [vara], mk_rel_sig varaTy) + let v_generic_hash_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericHashIntrinsic" , None , None , [vara], mk_hash_sig varaTy) let v_generic_hash_withc_inner_info = makeIntrinsicValRef(fslib_MFHashCompare_nleref, "GenericHashWithComparerIntrinsic" , None , None , [vara], mk_hash_withc_sig varaTy) @@ -1220,6 +1224,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.generic_hash_withc_outer_info = v_generic_hash_withc_outer_info member val generic_hash_inner_vref = ValRefForIntrinsic v_generic_hash_inner_info member val generic_hash_withc_inner_vref = ValRefForIntrinsic v_generic_hash_withc_inner_info + member val fsharpEqualityComparer_ER_Equals_vref = ValRefForIntrinsic v_FSharpEqualityComparer_ER_Equals_info + member val fsharpEqualityComparer_PER_Equals_vref = ValRefForIntrinsic v_FSharpEqualityComparer_PER_Equals_info + member val fsharpEqualityComparer_GetHashCode_vref = ValRefForIntrinsic v_FSharpEqualityComparer_GetHashCode_info member val reference_equality_inner_vref = ValRefForIntrinsic v_reference_equality_inner_info diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/ComparersRegression.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/ComparersRegression.fs index 4333e5316af..b9aa3b5a290 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/ComparersRegression.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/ComparersRegression.fs @@ -5183,41 +5183,47 @@ type GeneratedTestSuite () = [] member __.``SBytes.Collection.ArrayArray C.I.compare``() = validate (SBytes.Collection.ArrayArray) C.I.compare [| - 0;1;-1;1;1;-1;-1;-1;-1;-1;-1;0;-1;1;1;-1;-1;-1;-1;-1;1;1;0;1;1;-1;-1;-1;-1;-1;-1;-1;-1;0;-1;-1;-1;-1;-1;-1; - -1;-1;-1;1;0;-1;-1;-1;-1;-1;1;1;1;1;1;0;1;-1;1;1;1;1;1;1;1;-1;0;-1;1;1;1;1;1;1;1;1;1;0;1;1; - 1;1;1;1;1;-1;-1;-1;0;-1;1;1;1;1;1;-1;-1;-1;1;0 - |] + 0;-255;-127;-128;-129;-1;-1;-1;-1;-1;255;0;128;127;126;-1;-1; + -1;-1;-1;127;-128;0;-1;-2;-1;-1;-1;-1;-1;128;-127;1;0;-1;-1; + -1;-1;-1;-1;129;-126;2;1;0;-1;-1;-1;-1;-1;1;1;1;1;1;0;-255; + -127;-128;-129;1;1;1;1;1;255;0;128;127;126;1;1;1;1;1;127; + -128;0;-1;-2;1;1;1;1;1;128;-127;1;0;-1;1;1;1;1;1;129;-126; + 2;1;0|] [] member __.``SBytes.Collection.ArrayArray C.I.less_than``() = validate (SBytes.Collection.ArrayArray) C.I.less_than [| - 0;0;1;0;0;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1; - 1;1;1;0;0;1;1;1;1;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;1;0;0;0;0;0;0;0;0;0;0;0;0; - 0;0;0;0;0;1;1;1;0;1;0;0;0;0;0;1;1;1;0;0 + 0;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;0;1;0;1;1;1; + 1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;1;0;0;0;1;1;1;1;1;0;0; + 0;0;0;0;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0; + 1;1;0;0;0;0;0;0;1;0;0;1;0;0;0;0;0;0;1;0;0;0 |] [] member __.``SBytes.Collection.ArrayArray C.I.less_or_equal``() = validate (SBytes.Collection.ArrayArray) C.I.less_or_equal [| - 1;0;1;0;0;1;1;1;1;1;1;1;1;0;0;1;1;1;1;1;0;0;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; - 1;1;1;0;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;0;0;0;0;0;1;1;1;0;0;0;0;0;0;0;0;0;1;0;0; - 0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;1;1;1;0;1 + 1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;1;1;1;1;0;1;1;1;1;1; + 1;1;1;1;0;1;0;1;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;0; + 0;0;0;1;1;1;1;1;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;1; + 1;1;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;0;0;1 |] [] member __.``SBytes.Collection.ArrayArray C.I.greater_than``() = validate (SBytes.Collection.ArrayArray) C.I.greater_than [| - 0;1;0;1;1;0;0;0;0;0;0;0;0;1;1;0;0;0;0;0;1;1;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; - 0;0;0;1;0;0;0;0;0;0;1;1;1;1;1;0;1;0;1;1;1;1;1;1;1;0;0;0;1;1;1;1;1;1;1;1;1;0;1;1; - 1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;0;0;0;1;0 + 0;0;0;0;0;0;0;0;0;0;1;0;1;1;1;0;0;0;0;0;1;0;0;0;0;0; + 0;0;0;0;1;0;1;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;1; + 1;1;1;0;0;0;0;0;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0; + 0;0;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;1;1;0 |] [] member __.``SBytes.Collection.ArrayArray C.I.greater_or_equal``() = validate (SBytes.Collection.ArrayArray) C.I.greater_or_equal [| - 1;1;0;1;1;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0; - 0;0;0;1;1;0;0;0;0;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1; - 1;1;1;1;1;0;0;0;1;0;1;1;1;1;1;0;0;0;1;1 + 1;0;0;0;0;0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;0; + 0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;0;1;1;1;0;0;0;0;0;1;1; + 1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1; + 0;0;1;1;1;1;1;1;0;1;1;0;1;1;1;1;1;1;0;1;1;1 |] [] @@ -5247,41 +5253,48 @@ type GeneratedTestSuite () = [] member __.``SBytes.Collection.ArrayArray C.N.compare``() = validate (SBytes.Collection.ArrayArray) C.N.compare [| - 0;1;-1;1;1;-1;-1;-1;-1;-1;-1;0;-1;1;1;-1;-1;-1;-1;-1;1;1;0;1;1;-1;-1;-1;-1;-1;-1;-1;-1;0;-1;-1;-1;-1;-1;-1; - -1;-1;-1;1;0;-1;-1;-1;-1;-1;1;1;1;1;1;0;1;-1;1;1;1;1;1;1;1;-1;0;-1;1;1;1;1;1;1;1;1;1;0;1;1; - 1;1;1;1;1;-1;-1;-1;0;-1;1;1;1;1;1;-1;-1;-1;1;0 + 0;-255;-127;-128;-129;-1;-1;-1;-1;-1;255;0;128;127;126;-1;-1; + -1;-1;-1;127;-128;0;-1;-2;-1;-1;-1;-1;-1;128;-127;1;0;-1;-1; + -1;-1;-1;-1;129;-126;2;1;0;-1;-1;-1;-1;-1;1;1;1;1;1;0;-255; + -127;-128;-129;1;1;1;1;1;255;0;128;127;126;1;1;1;1;1;127; + -128;0;-1;-2;1;1;1;1;1;128;-127;1;0;-1;1;1;1;1;1;129;-126; + 2;1;0 |] [] member __.``SBytes.Collection.ArrayArray C.N.less_than``() = validate (SBytes.Collection.ArrayArray) C.N.less_than [| - 0;0;1;0;0;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1; - 1;1;1;0;0;1;1;1;1;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;1;0;0;0;0;0;0;0;0;0;0;0;0; - 0;0;0;0;0;1;1;1;0;1;0;0;0;0;0;1;1;1;0;0 + 0;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;0;1;0;1;1;1; + 1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;1;0;0;0;1;1;1;1;1;0;0; + 0;0;0;0;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0; + 1;1;0;0;0;0;0;0;1;0;0;1;0;0;0;0;0;0;1;0;0;0 |] [] member __.``SBytes.Collection.ArrayArray C.N.less_or_equal``() = validate (SBytes.Collection.ArrayArray) C.N.less_or_equal [| - 1;0;1;0;0;1;1;1;1;1;1;1;1;0;0;1;1;1;1;1;0;0;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; - 1;1;1;0;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;0;0;0;0;0;1;1;1;0;0;0;0;0;0;0;0;0;1;0;0; - 0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;1;1;1;0;1 + 1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;1;1;1;1;0;1;1;1;1;1; + 1;1;1;1;0;1;0;1;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;0; + 0;0;0;1;1;1;1;1;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;1; + 1;1;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;0;0;1 |] [] member __.``SBytes.Collection.ArrayArray C.N.greater_than``() = validate (SBytes.Collection.ArrayArray) C.N.greater_than [| - 0;1;0;1;1;0;0;0;0;0;0;0;0;1;1;0;0;0;0;0;1;1;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; - 0;0;0;1;0;0;0;0;0;0;1;1;1;1;1;0;1;0;1;1;1;1;1;1;1;0;0;0;1;1;1;1;1;1;1;1;1;0;1;1; - 1;1;1;1;1;0;0;0;0;0;1;1;1;1;1;0;0;0;1;0 + 0;0;0;0;0;0;0;0;0;0;1;0;1;1;1;0;0;0;0;0;1;0;0;0;0;0; + 0;0;0;0;1;0;1;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;1; + 1;1;1;0;0;0;0;0;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0; + 0;0;1;1;1;1;1;1;0;1;0;0;1;1;1;1;1;1;0;1;1;0 |] [] member __.``SBytes.Collection.ArrayArray C.N.greater_or_equal``() = validate (SBytes.Collection.ArrayArray) C.N.greater_or_equal [| - 1;1;0;1;1;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0; - 0;0;0;1;1;0;0;0;0;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1; - 1;1;1;1;1;0;0;0;1;0;1;1;1;1;1;0;0;0;1;1 + 1;0;0;0;0;0;0;0;0;0;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;0; + 0;0;0;0;1;0;1;1;0;0;0;0;0;0;1;0;1;1;1;0;0;0;0;0;1;1; + 1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1; + 0;0;1;1;1;1;1;1;0;1;1;0;1;1;1;1;1;1;0;1;1;1 |] [] diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 0f36e747976..ef363486f12 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2229,11 +2229,25 @@ Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputMu Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputSequenceEmptyString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_NoNegateMinValueString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean Equals(System.Object) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple2[T1,T2](System.Collections.IEqualityComparer, System.Tuple`2[T1,T2], System.Tuple`2[T1,T2]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3], System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4], System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5], System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_ER_Equals[T](T, T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_PER_Equals[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityERIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityWithComparerIntrinsic[T](System.Collections.IEqualityComparer, T, T) @@ -2250,6 +2264,7 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple2[T1,T2 Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FSharpEqualityComparer_GetHashCode[T](T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonWithComparerIntrinsic[T](System.Collections.IComparer, T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashIntrinsic[T](T) @@ -2257,6 +2272,8 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashWithCompa Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GetHashCode() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 LimitedGenericHashIntrinsic[T](Int32, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 PhysicalHashIntrinsic[T](T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T] +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T] Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.String ToString() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+IntrinsicFunctions: Boolean Equals(System.Object) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index 54038feab93..7f08a8900ac 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2316,11 +2316,25 @@ Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputMu Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_InputSequenceEmptyString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.String get_NoNegateMinValueString() Microsoft.FSharp.Core.LanguagePrimitives+ErrorStrings: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T]: System.Type GetType() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Boolean Equals(System.Object) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: Int32 GetHashCode() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] EqualityComparer +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Collections.Generic.EqualityComparer`1[T] get_EqualityComparer() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.String ToString() +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T]: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean Equals(System.Object) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple2[T1,T2](System.Collections.IEqualityComparer, System.Tuple`2[T1,T2], System.Tuple`2[T1,T2]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3], System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4], System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FastEqualsTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5], System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_ER_Equals[T](T, T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean FSharpEqualityComparer_PER_Equals[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityERIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Boolean GenericEqualityWithComparerIntrinsic[T](System.Collections.IEqualityComparer, T, T) @@ -2337,6 +2351,7 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple2[T1,T2 Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple3[T1,T2,T3](System.Collections.IEqualityComparer, System.Tuple`3[T1,T2,T3]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple4[T1,T2,T3,T4](System.Collections.IEqualityComparer, System.Tuple`4[T1,T2,T3,T4]) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FastHashTuple5[T1,T2,T3,T4,T5](System.Collections.IEqualityComparer, System.Tuple`5[T1,T2,T3,T4,T5]) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 FSharpEqualityComparer_GetHashCode[T](T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonIntrinsic[T](T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericComparisonWithComparerIntrinsic[T](System.Collections.IComparer, T, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashIntrinsic[T](T) @@ -2344,6 +2359,8 @@ Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GenericHashWithCompa Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 GetHashCode() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 LimitedGenericHashIntrinsic[T](Int32, T) Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Int32 PhysicalHashIntrinsic[T](T) +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_ER`1[T] +Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: Microsoft.FSharp.Core.LanguagePrimitives+HashCompare+FSharpEqualityComparer_PER`1[T] Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.String ToString() Microsoft.FSharp.Core.LanguagePrimitives+HashCompare: System.Type GetType() Microsoft.FSharp.Core.LanguagePrimitives+IntrinsicFunctions: Boolean Equals(System.Object) diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl index 22342da9b92..603c997764a 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/QueryExpressionStepping/Linq101Joins01.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly extern Utils { @@ -38,20 +38,20 @@ } .mresource public FSharpSignatureData.Linq101Joins01 { - // Offset: 0x00000000 Length: 0x000002F4 + // Offset: 0x00000000 Length: 0x00000326 } .mresource public FSharpOptimizationData.Linq101Joins01 { - // Offset: 0x000002F8 Length: 0x000000C3 + // Offset: 0x00000330 Length: 0x000000C3 } .module Linq101Joins01.exe -// MVID: {5A1F62A6-151B-685E-A745-0383A6621F5A} +// MVID: {5B2D78B8-151B-685E-A745-0383B8782D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x03830000 +// Image base: 0x027D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -81,7 +81,7 @@ // Code size 2 (0x2) .maxstack 8 .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 14,14 : 32,33 'C:\\visualfsharp\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\QueryExpressionStepping\\Linq101Joins01.fs' + .line 14,14 : 32,33 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\QueryExpressionStepping\\Linq101Joins01.fs' IL_0000: ldarg.1 IL_0001: ret } // end of method q@14::Invoke @@ -790,54 +790,67 @@ .method public strict virtual instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2,class [Utils]Utils/Product,string>,object> Invoke(class [Utils]Utils/Product _arg2) cil managed { - // Code size 69 (0x45) + // Code size 86 (0x56) .maxstack 9 .locals init ([0] class [Utils]Utils/Product p, - [1] string t) + [1] string t, + [2] object V_2, + [3] object V_3, + [4] object V_4, + [5] object V_5) .line 40,40 : 9,40 '' IL_0000: ldarg.1 IL_0001: stloc.0 .line 41,41 : 17,39 '' IL_0002: ldloc.0 IL_0003: box [Utils]Utils/Product - IL_0008: ldnull - IL_0009: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_000e: brfalse.s IL_0012 - - IL_0010: br.s IL_0014 - - IL_0012: br.s IL_001c + IL_0008: stloc.2 + IL_0009: ldnull + IL_000a: stloc.3 + IL_000b: ldloc.2 + IL_000c: stloc.s V_4 + IL_000e: ldloc.3 + IL_000f: stloc.s V_5 + IL_0011: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0016: ldloc.s V_4 + IL_0018: ldloc.s V_5 + IL_001a: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_001f: brfalse.s IL_0023 + + IL_0021: br.s IL_0025 + + IL_0023: br.s IL_002d .line 41,41 : 40,55 '' - IL_0014: ldstr "(No products)" + IL_0025: ldstr "(No products)" .line 100001,100001 : 0,0 '' - IL_0019: nop - IL_001a: br.s IL_0023 + IL_002a: nop + IL_002b: br.s IL_0034 .line 41,41 : 61,74 '' - IL_001c: ldloc.0 - IL_001d: callvirt instance string [Utils]Utils/Product::get_ProductName() + IL_002d: ldloc.0 + IL_002e: callvirt instance string [Utils]Utils/Product::get_ProductName() .line 100001,100001 : 0,0 '' - IL_0022: nop + IL_0033: nop .line 100001,100001 : 0,0 '' - IL_0023: stloc.1 + IL_0034: stloc.1 .line 42,42 : 9,22 '' - IL_0024: ldarg.0 - IL_0025: ldfld class [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder Linq101Joins01/'q4@40-4'::builder@ - IL_002a: ldarg.0 - IL_002b: ldfld string Linq101Joins01/'q4@40-4'::c - IL_0030: ldarg.0 - IL_0031: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1 Linq101Joins01/'q4@40-4'::ps - IL_0036: ldloc.0 - IL_0037: ldloc.1 - IL_0038: newobj instance void class [mscorlib]System.Tuple`4,class [Utils]Utils/Product,string>::.ctor(!0, + IL_0035: ldarg.0 + IL_0036: ldfld class [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder Linq101Joins01/'q4@40-4'::builder@ + IL_003b: ldarg.0 + IL_003c: ldfld string Linq101Joins01/'q4@40-4'::c + IL_0041: ldarg.0 + IL_0042: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1 Linq101Joins01/'q4@40-4'::ps + IL_0047: ldloc.0 + IL_0048: ldloc.1 + IL_0049: newobj instance void class [mscorlib]System.Tuple`4,class [Utils]Utils/Product,string>::.ctor(!0, !1, !2, !3) - IL_003d: tail. - IL_003f: callvirt instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2 [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder::Yield,class [Utils]Utils/Product,string>,object>(!!0) - IL_0044: ret + IL_004e: tail. + IL_0050: callvirt instance class [FSharp.Core]Microsoft.FSharp.Linq.QuerySource`2 [FSharp.Core]Microsoft.FSharp.Linq.QueryBuilder::Yield,class [Utils]Utils/Product,string>,object>(!!0) + IL_0055: ret } // end of method 'q4@40-4'::Invoke } // end of class 'q4@40-4' diff --git a/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl b/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl index 55664ad8b3a..17ce5430574 100644 --- a/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl +++ b/tests/fsharpqa/Source/CodeGen/EmittedIL/StaticInit/StaticInit_Struct01.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly StaticInit_Struct01 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.StaticInit_Struct01 { - // Offset: 0x00000000 Length: 0x000007B1 + // Offset: 0x00000000 Length: 0x000007B5 } .mresource public FSharpOptimizationData.StaticInit_Struct01 { - // Offset: 0x000007B8 Length: 0x0000021F + // Offset: 0x000007C0 Length: 0x0000021F } .module StaticInit_Struct01.dll -// MVID: {59B19250-05F6-D6CB-A745-03835092B159} +// MVID: {5B2D78C5-05F6-D6CB-A745-0383C5782D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02BA0000 +// Image base: 0x02C80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -71,7 +71,7 @@ .maxstack 5 .locals init ([0] valuetype StaticInit_Struct01/C& V_0) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 4,4 : 6,7 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\StaticInit\\StaticInit_Struct01.fs' + .line 4,4 : 6,7 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\CodeGen\\EmittedIL\\StaticInit\\StaticInit_Struct01.fs' IL_0000: ldarga.s obj IL_0002: stloc.0 IL_0003: call class [mscorlib]System.Collections.IComparer [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives::get_GenericComparer() @@ -256,20 +256,33 @@ instance bool Equals(valuetype StaticInit_Struct01/C obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 23 (0x17) - .maxstack 4 - .locals init ([0] valuetype StaticInit_Struct01/C& V_0) + // Code size 38 (0x26) + .maxstack 5 + .locals init ([0] valuetype StaticInit_Struct01/C& V_0, + [1] valuetype [mscorlib]System.DateTime V_1, + [2] valuetype [mscorlib]System.DateTime V_2, + [3] valuetype [mscorlib]System.DateTime V_3, + [4] valuetype [mscorlib]System.DateTime V_4) .line 4,4 : 6,7 '' IL_0000: ldarga.s obj IL_0002: stloc.0 IL_0003: ldarg.0 IL_0004: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s - IL_0009: ldloc.0 - IL_000a: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s - IL_000f: tail. - IL_0011: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0016: ret + IL_0009: stloc.1 + IL_000a: ldloc.0 + IL_000b: ldfld valuetype [mscorlib]System.DateTime StaticInit_Struct01/C::s + IL_0010: stloc.2 + IL_0011: ldloc.1 + IL_0012: stloc.3 + IL_0013: ldloc.2 + IL_0014: stloc.s V_4 + IL_0016: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_001b: ldloc.3 + IL_001c: ldloc.s V_4 + IL_001e: tail. + IL_0020: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_0025: ret } // end of method C::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl index 70335b05d20..cb04fa9fb46 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Compare07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Compare07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Compare07 { - // Offset: 0x00000000 Length: 0x0000089A + // Offset: 0x00000000 Length: 0x0000089E } .mresource public FSharpOptimizationData.Compare07 { - // Offset: 0x000008A0 Length: 0x00000692 + // Offset: 0x000008A8 Length: 0x0000069A } .module Compare07.dll -// MVID: {59B18AEE-05DE-F88E-A745-0383EE8AB159} +// MVID: {5B2D7B11-05DE-F88E-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x02BA0000 +// Image base: 0x02640000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Compare07.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Compare07/CompareMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl index 91247bff24d..e575063fd39 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals06.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals06 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals06 { - // Offset: 0x00000000 Length: 0x00000896 + // Offset: 0x00000000 Length: 0x0000089A } .mresource public FSharpOptimizationData.Equals06 { - // Offset: 0x000008A0 Length: 0x0000068E + // Offset: 0x000008A0 Length: 0x00000696 } .module Equals06.dll -// MVID: {59B18AEE-0759-31EC-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-31EC-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01B90000 +// Image base: 0x02980000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals06.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Equals06/EqualsMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl index 91f97c289a2..1ce42f2b348 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals07.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals07 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals07 { - // Offset: 0x00000000 Length: 0x0000022D + // Offset: 0x00000000 Length: 0x00000245 } .mresource public FSharpOptimizationData.Equals07 { - // Offset: 0x00000238 Length: 0x000000AF + // Offset: 0x00000250 Length: 0x000000AF } .module Equals07.dll -// MVID: {59B18AEE-0759-AE27-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-AE27-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01C80000 +// Image base: 0x02D80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,14 +57,14 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static bool f7() cil managed { - // Code size 68 (0x44) + // Code size 73 (0x49) .maxstack 5 .locals init ([0] bool x, [1] uint8[] t1, [2] uint8[] t2, [3] int32 i) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 5,5 : 8,29 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals07.fsx' + .line 5,5 : 8,29 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals07.fsx' IL_0000: ldc.i4.0 IL_0001: stloc.0 .line 6,6 : 8,35 '' @@ -90,26 +90,27 @@ .line 8,8 : 8,32 '' IL_002a: ldc.i4.0 IL_002b: stloc.3 - IL_002c: br.s IL_003a + IL_002c: br.s IL_003f .line 9,9 : 12,26 '' - IL_002e: ldloc.1 - IL_002f: ldloc.2 - IL_0030: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 + IL_002e: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0033: ldloc.1 + IL_0034: ldloc.2 + IL_0035: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_003a: stloc.0 + IL_003b: ldloc.3 + IL_003c: ldc.i4.1 + IL_003d: add + IL_003e: stloc.3 .line 8,8 : 8,32 '' - IL_003a: ldloc.3 - IL_003b: ldc.i4 0x989681 - IL_0040: blt.s IL_002e + IL_003f: ldloc.3 + IL_0040: ldc.i4 0x989681 + IL_0045: blt.s IL_002e .line 10,10 : 8,9 '' - IL_0042: ldloc.0 - IL_0043: ret + IL_0047: ldloc.0 + IL_0048: ret } // end of method EqualsMicroPerfAndCodeGenerationTests::f7 } // end of class EqualsMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl index 55da6ee102e..39e19138617 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Equals08.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Equals08 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Equals08 { - // Offset: 0x00000000 Length: 0x0000022D + // Offset: 0x00000000 Length: 0x00000245 } .mresource public FSharpOptimizationData.Equals08 { - // Offset: 0x00000238 Length: 0x000000AF + // Offset: 0x00000250 Length: 0x000000AF } .module Equals08.dll -// MVID: {59B18AEE-0759-659E-A745-0383EE8AB159} +// MVID: {5B2D7B11-0759-659E-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01090000 +// Image base: 0x02FD0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,14 +57,14 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static bool f8() cil managed { - // Code size 68 (0x44) + // Code size 73 (0x49) .maxstack 5 .locals init ([0] bool x, [1] int32[] t1, [2] int32[] t2, [3] int32 i) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 5,5 : 8,29 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals08.fsx' + .line 5,5 : 8,29 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Equals08.fsx' IL_0000: ldc.i4.0 IL_0001: stloc.0 .line 6,6 : 8,31 '' @@ -90,26 +90,27 @@ .line 8,8 : 8,32 '' IL_002a: ldc.i4.0 IL_002b: stloc.3 - IL_002c: br.s IL_003a + IL_002c: br.s IL_003f .line 9,9 : 12,26 '' - IL_002e: ldloc.1 - IL_002f: ldloc.2 - IL_0030: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityIntrinsic(!!0, - !!0) - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 + IL_002e: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_0033: ldloc.1 + IL_0034: ldloc.2 + IL_0035: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_003a: stloc.0 + IL_003b: ldloc.3 + IL_003c: ldc.i4.1 + IL_003d: add + IL_003e: stloc.3 .line 8,8 : 8,32 '' - IL_003a: ldloc.3 - IL_003b: ldc.i4 0x989681 - IL_0040: blt.s IL_002e + IL_003f: ldloc.3 + IL_0040: ldc.i4 0x989681 + IL_0045: blt.s IL_002e .line 10,10 : 8,9 '' - IL_0042: ldloc.0 - IL_0043: ret + IL_0047: ldloc.0 + IL_0048: ret } // end of method EqualsMicroPerfAndCodeGenerationTests::f8 } // end of class EqualsMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl index df7b115d207..726d2aea5e8 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash09.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash09 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash09 { - // Offset: 0x00000000 Length: 0x0000088E + // Offset: 0x00000000 Length: 0x00000892 } .mresource public FSharpOptimizationData.Hash09 { - // Offset: 0x00000898 Length: 0x00000686 + // Offset: 0x00000898 Length: 0x0000068E } .module Hash09.dll -// MVID: {59B18AEE-9642-77DB-A745-0383EE8AB159} +// MVID: {5B2D7B11-9642-77DB-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00690000 +// Image base: 0x02990000 // =============== CLASS MEMBERS DECLARATION =================== @@ -187,7 +187,7 @@ [4] !a V_4, [5] !a V_5) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 16707566,16707566 : 0,0 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' + .line 16707566,16707566 : 0,0 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash09.fsx' IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un @@ -560,8 +560,8 @@ instance bool Equals(class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 obj) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - // Code size 77 (0x4d) - .maxstack 4 + // Code size 87 (0x57) + .maxstack 5 .locals init ([0] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_0, [1] class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1 V_1, [2] !a V_2, @@ -570,13 +570,13 @@ IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un - IL_0004: brfalse.s IL_0045 + IL_0004: brfalse.s IL_004f .line 16707566,16707566 : 0,0 '' IL_0006: ldarg.1 IL_0007: ldnull IL_0008: cgt.un - IL_000a: brfalse.s IL_0043 + IL_000a: brfalse.s IL_004d .line 16707566,16707566 : 0,0 '' IL_000c: ldarg.0 @@ -592,41 +592,43 @@ IL_0019: ldloc.1 IL_001a: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item1 IL_001f: stloc.3 - IL_0020: ldloc.2 - IL_0021: ldloc.3 - IL_0022: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0027: brfalse.s IL_0041 - - .line 16707566,16707566 : 0,0 '' - IL_0029: ldloc.0 - IL_002a: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_002f: stloc.2 - IL_0030: ldloc.1 - IL_0031: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 - IL_0036: stloc.3 - IL_0037: ldloc.2 - IL_0038: ldloc.3 - IL_0039: tail. - IL_003b: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericEqualityERIntrinsic(!!0, - !!0) - IL_0040: ret + IL_0020: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0025: ldloc.2 + IL_0026: ldloc.3 + IL_0027: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_002c: brfalse.s IL_004b + + .line 16707566,16707566 : 0,0 '' + IL_002e: ldloc.0 + IL_002f: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_0034: stloc.2 + IL_0035: ldloc.1 + IL_0036: ldfld !0 class Hash09/HashMicroPerfAndCodeGenerationTests/GenericKey`1::item2 + IL_003b: stloc.3 + IL_003c: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_ER`1::get_EqualityComparer() + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: tail. + IL_0045: callvirt instance bool class [mscorlib]System.Collections.Generic.EqualityComparer`1::Equals(!0, + !0) + IL_004a: ret .line 16707566,16707566 : 0,0 '' - IL_0041: ldc.i4.0 - IL_0042: ret + IL_004b: ldc.i4.0 + IL_004c: ret .line 16707566,16707566 : 0,0 '' - IL_0043: ldc.i4.0 - IL_0044: ret + IL_004d: ldc.i4.0 + IL_004e: ret .line 16707566,16707566 : 0,0 '' - IL_0045: ldarg.1 - IL_0046: ldnull - IL_0047: cgt.un - IL_0049: ldc.i4.0 - IL_004a: ceq - IL_004c: ret + IL_004f: ldarg.1 + IL_0050: ldnull + IL_0051: cgt.un + IL_0053: ldc.i4.0 + IL_0054: ceq + IL_0056: ret } // end of method GenericKey`1::Equals .method public hidebysig virtual final diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl index 20c3ceeb8f6..029eaf7814a 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash10.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash10 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash10 { - // Offset: 0x00000000 Length: 0x00000219 + // Offset: 0x00000000 Length: 0x00000231 } .mresource public FSharpOptimizationData.Hash10 { - // Offset: 0x00000220 Length: 0x000000A9 + // Offset: 0x00000238 Length: 0x000000A9 } .module Hash10.dll -// MVID: {59B18AEE-9661-78B4-A745-0383EE8AB159} +// MVID: {5B2D7B11-9661-78B4-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01080000 +// Image base: 0x01690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,13 +57,13 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static void f7() cil managed { - // Code size 44 (0x2c) + // Code size 49 (0x31) .maxstack 5 .locals init ([0] uint8[] arr, [1] int32 i, [2] int32 V_2) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 6,6 : 8,36 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash10.fsx' + .line 6,6 : 8,36 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash10.fsx' IL_0000: ldc.i4.0 IL_0001: ldc.i4.1 IL_0002: ldc.i4.s 100 @@ -76,22 +76,23 @@ .line 7,7 : 8,32 '' IL_0014: ldc.i4.0 IL_0015: stloc.1 - IL_0016: br.s IL_0023 + IL_0016: br.s IL_0028 .line 8,8 : 12,30 '' - IL_0018: ldloc.0 - IL_0019: call int32 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericHashIntrinsic(!!0) - IL_001e: stloc.2 - IL_001f: ldloc.1 - IL_0020: ldc.i4.1 - IL_0021: add - IL_0022: stloc.1 + IL_0018: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_001d: ldloc.0 + IL_001e: callvirt instance int32 class [mscorlib]System.Collections.Generic.EqualityComparer`1::GetHashCode(!0) + IL_0023: stloc.2 + IL_0024: ldloc.1 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.1 .line 7,7 : 8,32 '' - IL_0023: ldloc.1 - IL_0024: ldc.i4 0x989681 - IL_0029: blt.s IL_0018 + IL_0028: ldloc.1 + IL_0029: ldc.i4 0x989681 + IL_002e: blt.s IL_0018 - IL_002b: ret + IL_0030: ret } // end of method HashMicroPerfAndCodeGenerationTests::f7 } // end of class HashMicroPerfAndCodeGenerationTests diff --git a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl index f4eb5020175..9848907a1b1 100644 --- a/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl +++ b/tests/fsharpqa/Source/Optimizations/GenericComparison/Hash11.il.bsl @@ -13,7 +13,7 @@ .assembly extern FSharp.Core { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:4:1:0 + .ver 4:4:3:0 } .assembly Hash11 { @@ -29,20 +29,20 @@ } .mresource public FSharpSignatureData.Hash11 { - // Offset: 0x00000000 Length: 0x00000219 + // Offset: 0x00000000 Length: 0x00000231 } .mresource public FSharpOptimizationData.Hash11 { - // Offset: 0x00000220 Length: 0x000000A9 + // Offset: 0x00000238 Length: 0x000000A9 } .module Hash11.dll -// MVID: {59B18AEE-9661-78D3-A745-0383EE8AB159} +// MVID: {5B2D7B11-9661-78D3-A745-0383117B2D5B} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x002D0000 +// Image base: 0x027C0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -57,13 +57,13 @@ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static void f8() cil managed { - // Code size 44 (0x2c) + // Code size 49 (0x31) .maxstack 5 .locals init ([0] int32[] arr, [1] int32 i, [2] int32 V_2) .language '{AB4F38C9-B6E6-43BA-BE3B-58080B2CCCE3}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}' - .line 6,6 : 8,32 'C:\\GitHub\\dsyme\\visualfsharp\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash11.fsx' + .line 6,6 : 8,32 'C:\\src\\manofstick\\visualfsharp-nobox\\tests\\fsharpqa\\Source\\Optimizations\\GenericComparison\\Hash11.fsx' IL_0000: ldc.i4.0 IL_0001: ldc.i4.1 IL_0002: ldc.i4.s 100 @@ -76,22 +76,23 @@ .line 7,7 : 8,32 '' IL_0014: ldc.i4.0 IL_0015: stloc.1 - IL_0016: br.s IL_0023 + IL_0016: br.s IL_0028 .line 8,8 : 12,30 '' - IL_0018: ldloc.0 - IL_0019: call int32 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare::GenericHashIntrinsic(!!0) - IL_001e: stloc.2 - IL_001f: ldloc.1 - IL_0020: ldc.i4.1 - IL_0021: add - IL_0022: stloc.1 + IL_0018: call class [mscorlib]System.Collections.Generic.EqualityComparer`1 class [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/HashCompare/FSharpEqualityComparer_PER`1::get_EqualityComparer() + IL_001d: ldloc.0 + IL_001e: callvirt instance int32 class [mscorlib]System.Collections.Generic.EqualityComparer`1::GetHashCode(!0) + IL_0023: stloc.2 + IL_0024: ldloc.1 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.1 .line 7,7 : 8,32 '' - IL_0023: ldloc.1 - IL_0024: ldc.i4 0x989681 - IL_0029: blt.s IL_0018 + IL_0028: ldloc.1 + IL_0029: ldc.i4 0x989681 + IL_002e: blt.s IL_0018 - IL_002b: ret + IL_0030: ret } // end of method HashMicroPerfAndCodeGenerationTests::f8 } // end of class HashMicroPerfAndCodeGenerationTests