Skip to content

Commit c9d55e3

Browse files
authored
Merge pull request #14551 from dotnet/merges/main-to-release/dev17.5
Merge main to release/dev17.5
2 parents 8fdf58a + ad9b402 commit c9d55e3

File tree

5 files changed

+50
-5
lines changed

5 files changed

+50
-5
lines changed

src/Compiler/Checking/AugmentWithHashCompare.fs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,16 @@ let MakeBindingsForEqualityWithComparerAugmentation (g: TcGlobals) (tycon: Tycon
996996
// build the hash rhs
997997
let withcGetHashCodeExpr =
998998
let compv, compe = mkCompGenLocal m "comp" g.IEqualityComparer_ty
999-
let thisv, hashe = hashf g tcref tycon compe
999+
1000+
// Special case List<T> type to avoid StackOverflow exception , call custom hash code instead
1001+
let thisv,hashe =
1002+
if tyconRefEq g tcref g.list_tcr_canon && tycon.HasMember g "CustomHashCode" [g.IEqualityComparer_ty] then
1003+
let customCodeVal = (tycon.TryGetMember g "CustomHashCode" [g.IEqualityComparer_ty]).Value
1004+
let tinst, ty = mkMinimalTy g tcref
1005+
let thisv, thise = mkThisVar g m ty
1006+
thisv,mkApps g ((exprForValRef m customCodeVal, customCodeVal.Type), (if isNil tinst then [] else [tinst]), [thise; compe], m)
1007+
else
1008+
hashf g tcref tycon compe
10001009
mkLambdas g m tps [thisv; compv] (hashe, g.int_ty)
10011010

10021011
// build the equals rhs

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9573,11 +9573,11 @@ type Entity with
95739573
List.lengthsEqAndForall2 (typeEquiv g) (List.map fst argInfos) argTys &&
95749574
membInfo.MemberFlags.IsOverrideOrExplicitImpl
95759575
| _ -> false)
9576-
9577-
member tycon.HasMember g nm argTys =
9576+
9577+
member tycon.TryGetMember g nm argTys =
95789578
tycon.TypeContents.tcaug_adhoc
95799579
|> NameMultiMap.find nm
9580-
|> List.exists (fun vref ->
9580+
|> List.tryFind (fun vref ->
95819581
match vref.MemberInfo with
95829582
| None -> false
95839583
| _ ->
@@ -9586,7 +9586,8 @@ type Entity with
95869586
match argInfos with
95879587
| [argInfos] -> List.lengthsEqAndForall2 (typeEquiv g) (List.map fst argInfos) argTys
95889588
| _ -> false)
9589-
9589+
9590+
member tycon.HasMember g nm argTys = (tycon.TryGetMember g nm argTys).IsSome
95909591

95919592
type EntityRef with
95929593
member tcref.HasInterface g ty = tcref.Deref.HasInterface g ty

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,6 +2449,8 @@ type Entity with
24492449

24502450
member HasMember: TcGlobals -> string -> TType list -> bool
24512451

2452+
member internal TryGetMember: TcGlobals -> string -> TType list -> ValRef option
2453+
24522454
type EntityRef with
24532455

24542456
member HasInterface: TcGlobals -> TType -> bool

src/FSharp.Core/prim-types.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3912,6 +3912,16 @@ namespace Microsoft.FSharp.Collections
39123912
type List<'T> =
39133913
| ([]) : 'T list
39143914
| ( :: ) : Head: 'T * Tail: 'T list -> 'T list
3915+
member private this.CustomHashCode(c:IEqualityComparer) =
3916+
let rec loop l acc position =
3917+
match l with
3918+
| [] -> acc
3919+
| h::t ->
3920+
let hashOfH = GenericHashWithComparer c h
3921+
let acc = LanguagePrimitives.HashCompare.HashCombine position acc hashOfH
3922+
loop t acc (position+1)
3923+
3924+
loop this 0 0
39153925
interface IEnumerable<'T>
39163926
interface IEnumerable
39173927
interface IReadOnlyCollection<'T>

tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListType.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,29 @@ type ListType() =
120120
Assert.AreEqual("[1; 2; 3]", [1; 2; 3].ToString())
121121
Assert.AreEqual("[]", [].ToString())
122122
Assert.AreEqual("[]", ([] : decimal list list).ToString())
123+
124+
[<Fact>]
125+
member this.HashCodeNotThrowingStackOverflow() =
126+
let l = 1 :: 2 :: [0.. 35_000]
127+
let hash = l.GetHashCode()
128+
129+
let l2 = [1;2] @ [0.. 35_000]
130+
let hash2 = l.GetHashCode()
131+
132+
Assert.AreEqual(hash,hash2)
133+
134+
[<Fact>]
135+
member this.HashCodeDoesNotThrowOnListOfNullStrings() =
136+
let l = ["1";"2";null;null]
137+
Assert.AreEqual(l.GetHashCode(),l.GetHashCode())
138+
139+
[<Fact>]
140+
member this.HashCodeIsDifferentForListsWithSamePrefix() =
141+
let sharedPrefix = [0..500]
142+
let l1 = sharedPrefix @ [1]
143+
let l2 = sharedPrefix @ [2]
144+
145+
Assert.AreNotEqual(l1.GetHashCode(),l2.GetHashCode())
123146

124147
[<Fact>]
125148
member this.ObjectEquals() =

0 commit comments

Comments
 (0)