|
1 | 1 | module internal Internal.Utilities.TypeHashing |
2 | 2 |
|
3 | 3 | open Internal.Utilities.Rational |
4 | | -open Internal.Utilities.Library |
5 | 4 | open FSharp.Compiler.AbstractIL.IL |
6 | 5 | open FSharp.Compiler.Syntax |
7 | 6 | open FSharp.Compiler.TcGlobals |
8 | 7 | open FSharp.Compiler.Text |
9 | 8 | open FSharp.Compiler.TypedTree |
10 | 9 | open FSharp.Compiler.TypedTreeBasics |
11 | 10 | open FSharp.Compiler.TypedTreeOps |
12 | | -open System.Collections.Immutable |
13 | 11 |
|
14 | 12 | type ObserverVisibility = |
15 | 13 | | PublicOnly |
@@ -126,6 +124,7 @@ module HashAccessibility = |
126 | 124 | | _ -> true |
127 | 125 |
|
128 | 126 | module rec HashTypes = |
| 127 | + |
129 | 128 | /// Hash a reference to a type |
130 | 129 | let hashTyconRef tcref = hashTyconRefImpl tcref |
131 | 130 |
|
@@ -378,110 +377,159 @@ module HashTastMemberOrVals = |
378 | 377 | /// * Uses per-compilation stamps (entities, typars, anon records, measures). |
379 | 378 | /// * Emits shape for union cases (declaring type stamp + case name), tuple structness, |
380 | 379 | /// function arrows, forall binders, nullness, measures, generic arguments. |
381 | | -/// * Unknown/variable nullness => NeverEqual token to force inequality (avoid unsound hits). |
| 380 | +/// * Does not include type constraints. |
382 | 381 | /// |
383 | 382 | /// Non-goals: |
384 | 383 | /// * Cross-compilation stability. |
385 | 384 | /// * Perfect canonicalisation or alpha-equivalence collapsing. |
386 | 385 | /// |
387 | 386 | /// </summary> |
388 | 387 | module StructuralUtilities = |
389 | | - [<Struct; CustomEquality; NoComparison>] |
390 | | - type NeverEqual = |
391 | | - struct |
392 | | - interface System.IEquatable<NeverEqual> with |
393 | | - member _.Equals _ = false |
394 | | - |
395 | | - override _.Equals _ = false |
396 | | - override _.GetHashCode() = 0 |
397 | | - end |
398 | | - |
399 | | - static member Singleton = NeverEqual() |
| 388 | + open Internal.Utilities.Library.Extras |
400 | 389 |
|
401 | 390 | [<Struct; NoComparison; RequireQualifiedAccess>] |
402 | 391 | type TypeToken = |
403 | 392 | | Stamp of stamp: Stamp |
404 | 393 | | UCase of name: string |
405 | 394 | | Nullness of nullness: NullnessInfo |
| 395 | + | NullnessUnsolved |
406 | 396 | | TupInfo of b: bool |
| 397 | + | Forall of int |
407 | 398 | | MeasureOne |
408 | 399 | | MeasureRational of int * int |
409 | | - | NeverEqual of never: NeverEqual |
| 400 | + | Solved of int |
| 401 | + | Unsolved of int |
| 402 | + | Rigid of int |
410 | 403 |
|
411 | 404 | type TypeStructure = |
412 | | - | TypeStructure of TypeToken[] |
413 | | - | PossiblyInfinite of never: NeverEqual |
| 405 | + | Stable of TypeToken[] |
| 406 | + | Unstable of TypeToken[] |
| 407 | + | PossiblyInfinite |
| 408 | + |
| 409 | + type private EmitContext = |
| 410 | + { |
| 411 | + typarMap: System.Collections.Generic.Dictionary<Stamp, int> |
| 412 | + emitNullness: bool |
| 413 | + mutable stable: bool |
| 414 | + } |
414 | 415 |
|
415 | | - let inline toNullnessToken (n: Nullness) = |
416 | | - match n.TryEvaluate() with |
417 | | - | ValueSome k -> TypeToken.Nullness k |
418 | | - | _ -> TypeToken.NeverEqual NeverEqual.Singleton |
| 416 | + let private emitNullness env (n: Nullness) = |
| 417 | + seq { |
| 418 | + if env.emitNullness then |
| 419 | + env.stable <- false // |
| 420 | + |
| 421 | + match n.TryEvaluate() with |
| 422 | + | ValueSome k -> TypeToken.Nullness k |
| 423 | + | ValueNone -> TypeToken.NullnessUnsolved |
| 424 | + } |
419 | 425 |
|
420 | | - let rec private accumulateMeasure (m: Measure) = |
| 426 | + let rec private emitMeasure (m: Measure) = |
421 | 427 | seq { |
422 | 428 | match m with |
423 | 429 | | Measure.Var mv -> TypeToken.Stamp mv.Stamp |
424 | 430 | | Measure.Const(tcref, _) -> TypeToken.Stamp tcref.Stamp |
425 | 431 | | Measure.Prod(m1, m2, _) -> |
426 | | - yield! accumulateMeasure m1 |
427 | | - yield! accumulateMeasure m2 |
428 | | - | Measure.Inv m1 -> yield! accumulateMeasure m1 |
| 432 | + yield! emitMeasure m1 |
| 433 | + yield! emitMeasure m2 |
| 434 | + | Measure.Inv m1 -> yield! emitMeasure m1 |
429 | 435 | | Measure.One _ -> TypeToken.MeasureOne |
430 | 436 | | Measure.RationalPower(m1, r) -> |
431 | | - yield! accumulateMeasure m1 |
| 437 | + yield! emitMeasure m1 |
432 | 438 | TypeToken.MeasureRational(GetNumerator r, GetDenominator r) |
433 | 439 | } |
434 | 440 |
|
435 | | - let rec private accumulateTType (ty: TType) = |
| 441 | + and private emitTType (env: EmitContext) (ty: TType) = |
436 | 442 | seq { |
437 | 443 | match ty with |
438 | 444 | | TType_ucase(u, tinst) -> |
439 | 445 | TypeToken.Stamp u.TyconRef.Stamp |
440 | 446 | TypeToken.UCase u.CaseName |
441 | 447 |
|
442 | 448 | for arg in tinst do |
443 | | - yield! accumulateTType arg |
| 449 | + yield! emitTType env arg |
444 | 450 |
|
445 | 451 | | TType_app(tcref, tinst, n) -> |
446 | 452 | TypeToken.Stamp tcref.Stamp |
447 | | - toNullnessToken n |
| 453 | + yield! emitNullness env n |
448 | 454 |
|
449 | 455 | for arg in tinst do |
450 | | - yield! accumulateTType arg |
| 456 | + yield! emitTType env arg |
| 457 | + |
451 | 458 | | TType_anon(info, tys) -> |
452 | 459 | TypeToken.Stamp info.Stamp |
453 | 460 |
|
454 | 461 | for arg in tys do |
455 | | - yield! accumulateTType arg |
| 462 | + yield! emitTType env arg |
| 463 | + |
456 | 464 | | TType_tuple(tupInfo, tys) -> |
457 | 465 | TypeToken.TupInfo(evalTupInfoIsStruct tupInfo) |
458 | 466 |
|
459 | 467 | for arg in tys do |
460 | | - yield! accumulateTType arg |
| 468 | + yield! emitTType env arg |
| 469 | + |
461 | 470 | | TType_forall(tps, tau) -> |
462 | 471 | for tp in tps do |
463 | | - TypeToken.Stamp tp.Stamp |
| 472 | + env.typarMap.[tp.Stamp] <- env.typarMap.Count |
| 473 | + |
| 474 | + TypeToken.Forall tps.Length |
| 475 | + |
| 476 | + yield! emitTType env tau |
464 | 477 |
|
465 | | - yield! accumulateTType tau |
466 | 478 | | TType_fun(d, r, n) -> |
467 | | - yield! accumulateTType d |
468 | | - yield! accumulateTType r |
469 | | - toNullnessToken n |
| 479 | + yield! emitTType env d |
| 480 | + yield! emitTType env r |
| 481 | + yield! emitNullness env n |
| 482 | + |
470 | 483 | | TType_var(r, n) -> |
471 | | - TypeToken.Stamp r.Stamp |
472 | | - toNullnessToken n |
473 | | - | TType_measure m -> yield! accumulateMeasure m |
| 484 | + yield! emitNullness env n |
| 485 | + |
| 486 | + let typarId = |
| 487 | + match env.typarMap.TryGetValue r.Stamp with |
| 488 | + | true, idx -> idx |
| 489 | + | _ -> |
| 490 | + let idx = env.typarMap.Count |
| 491 | + env.typarMap.[r.Stamp] <- idx |
| 492 | + idx |
| 493 | + |
| 494 | + // Solved may become unsolved, in case of Trace.Undo. |
| 495 | + env.stable <- false |
| 496 | + |
| 497 | + match r.Solution with |
| 498 | + | Some ty -> yield! emitTType env ty |
| 499 | + | None -> |
| 500 | + if r.Rigidity = TyparRigidity.Rigid then |
| 501 | + TypeToken.Rigid typarId |
| 502 | + else |
| 503 | + TypeToken.Unsolved typarId |
| 504 | + |
| 505 | + | TType_measure m -> yield! emitMeasure m |
474 | 506 | } |
475 | 507 |
|
476 | | - // If the sequence got too long, just drop it, we could be dealing with an infinite type. |
477 | | - let private toTypeStructure tokens = |
478 | | - let tokens = tokens |> Seq.truncate 256 |> Array.ofSeq |
479 | | - |
480 | | - if tokens.Length = 256 then |
481 | | - PossiblyInfinite NeverEqual.Singleton |
482 | | - else |
483 | | - TypeStructure tokens |
484 | | - |
485 | | - /// Get the full structure of a type as a sequence of tokens, suitable for equality |
486 | | - let getTypeStructure = |
487 | | - Extras.WeakMap.getOrCreate (fun ty -> accumulateTType ty |> toTypeStructure) |
| 508 | + let private getTypeStructureOfStrippedType (ty: TType) = |
| 509 | + |
| 510 | + let env = |
| 511 | + { |
| 512 | + typarMap = System.Collections.Generic.Dictionary<Stamp, int>() |
| 513 | + emitNullness = false |
| 514 | + stable = true |
| 515 | + } |
| 516 | + |
| 517 | + let tokens = emitTType env ty |> Seq.truncate 256 |> Seq.toArray |
| 518 | + |
| 519 | + // If the sequence got too long, just drop it, we could be dealing with an infinite type. |
| 520 | + if tokens.Length = 256 then PossiblyInfinite |
| 521 | + elif not env.stable then Unstable tokens |
| 522 | + else Stable tokens |
| 523 | + |
| 524 | + // Speed up repeated calls by memoizing results for types that yield a stable structure. |
| 525 | + let private memoize = |
| 526 | + WeakMap.cacheConditionally |
| 527 | + (function |
| 528 | + | Stable _ -> true |
| 529 | + | _ -> false) |
| 530 | + getTypeStructureOfStrippedType |
| 531 | + |
| 532 | + let tryGetTypeStructureOfStrippedType ty = |
| 533 | + match memoize ty with |
| 534 | + | PossiblyInfinite -> ValueNone |
| 535 | + | ts -> ValueSome ts |
0 commit comments