Skip to content

Commit 05500b8

Browse files
authored
Enable TypeSubsumptionCache for IDE use (#18499)
1 parent 34dbfc1 commit 05500b8

File tree

16 files changed

+634
-69
lines changed

16 files changed

+634
-69
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.300.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* Type parameter constraint `null` in generic code will now automatically imply `not struct` ([Issue #18320](https://github.com/dotnet/fsharp/issues/18320), [PR #18323](https://github.com/dotnet/fsharp/pull/18323))
4141
* Add a switch to determine whether to generate a default implementation body for overridden method when completing. [PR #18341](https://github.com/dotnet/fsharp/pull/18341)
4242
* Use a more accurate range for CE Combine methods. [PR #18394](https://github.com/dotnet/fsharp/pull/18394)
43+
* Enable TypeSubsumptionCache for IDE use. [PR #18499](https://github.com/dotnet/fsharp/pull/18499)
4344

4445

4546
### Changed

src/Compiler/Checking/TypeRelations.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ let TypesFeasiblyEquivStripMeasures g amap m ty1 ty2 =
102102
TypesFeasiblyEquivalent true 0 g amap m ty1 ty2
103103

104104
let inline TryGetCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key =
105-
if g.compilationMode = CompilationMode.OneOff && g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
105+
if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
106106
match amap.TypeSubsumptionCache.TryGetValue(key) with
107107
| true, subsumes ->
108108
ValueSome subsumes
@@ -112,8 +112,8 @@ let inline TryGetCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key =
112112
ValueNone
113113

114114
let inline UpdateCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key subsumes : unit =
115-
if g.compilationMode = CompilationMode.OneOff && g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
116-
amap.TypeSubsumptionCache[key] <- subsumes
115+
if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
116+
amap.TypeSubsumptionCache.TryAdd(key, subsumes) |> ignore
117117

118118
/// The feasible coercion relation. Part of the language spec.
119119
let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1: TType) (canCoerce: CanCoerce) (ty2: TType) =
@@ -125,7 +125,7 @@ let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1:
125125
let ty2 = stripTyEqns g ty2
126126

127127
// Check if language feature supported
128-
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce, g)
128+
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce)
129129

130130
match TryGetCachedTypeSubsumption g amap key with
131131
| ValueSome subsumes ->

src/Compiler/Checking/import.fs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@ module internal FSharp.Compiler.Import
66
open System.Collections.Concurrent
77
open System.Collections.Generic
88
open System.Collections.Immutable
9-
open FSharp.Compiler.Text.Range
9+
open System.Diagnostics
10+
1011
open Internal.Utilities.Library
1112
open Internal.Utilities.Library.Extras
1213
open Internal.Utilities.TypeHashing
13-
open Internal.Utilities.TypeHashing.HashTypes
14+
1415
open FSharp.Compiler
1516
open FSharp.Compiler.AbstractIL.IL
1617
open FSharp.Compiler.CompilerGlobalState
1718
open FSharp.Compiler.DiagnosticsLogger
1819
open FSharp.Compiler.SyntaxTreeOps
1920
open FSharp.Compiler.Text
21+
open FSharp.Compiler.Text.Range
2022
open FSharp.Compiler.Xml
2123
open FSharp.Compiler.TypedTree
2224
open FSharp.Compiler.TypedTreeBasics
2325
open FSharp.Compiler.TypedTreeOps
2426
open FSharp.Compiler.TcGlobals
27+
open FSharp.Compiler.Caches
2528

2629
#if !NO_TYPEPROVIDERS
2730
open FSharp.Compiler.TypeProviders
@@ -52,18 +55,18 @@ type CanCoerce =
5255
| CanCoerce
5356
| NoCoerce
5457

55-
type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
58+
[<Struct; NoComparison; CustomEquality; DebuggerDisplay("{ToString()}")>]
59+
type TTypeCacheKey =
5660

5761
val ty1: TType
5862
val ty2: TType
5963
val canCoerce: CanCoerce
60-
val tcGlobals: TcGlobals
6164

62-
private new (ty1, ty2, canCoerce, tcGlobals) =
63-
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce; tcGlobals = tcGlobals }
65+
private new (ty1, ty2, canCoerce) =
66+
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce }
6467

65-
static member FromStrippedTypes (ty1, ty2, canCoerce, tcGlobals) =
66-
TTypeCacheKey(ty1, ty2, canCoerce, tcGlobals)
68+
static member FromStrippedTypes (ty1, ty2, canCoerce) =
69+
TTypeCacheKey(ty1, ty2, canCoerce)
6770

6871
interface System.IEquatable<TTypeCacheKey> with
6972
member this.Equals other =
@@ -72,23 +75,24 @@ type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
7275
elif this.ty1 === other.ty1 && this.ty2 === other.ty2 then
7376
true
7477
else
75-
stampEquals this.tcGlobals this.ty1 other.ty1
76-
&& stampEquals this.tcGlobals this.ty2 other.ty2
78+
HashStamps.stampEquals this.ty1 other.ty1
79+
&& HashStamps.stampEquals this.ty2 other.ty2
7780

7881
override this.Equals(other:objnull) =
7982
match other with
8083
| :? TTypeCacheKey as p -> (this :> System.IEquatable<TTypeCacheKey>).Equals p
8184
| _ -> false
8285

83-
override this.GetHashCode() : int =
84-
let g = this.tcGlobals
85-
86-
let ty1Hash = combineHash (hashStamp g this.ty1) (hashTType g this.ty1)
87-
let ty2Hash = combineHash (hashStamp g this.ty2) (hashTType g this.ty2)
86+
override this.GetHashCode () : int =
87+
HashStamps.hashTType this.ty1
88+
|> pipeToHash (HashStamps.hashTType this.ty2)
89+
|> pipeToHash (hash this.canCoerce)
8890

89-
let combined = combineHash (combineHash ty1Hash ty2Hash) (hash this.canCoerce)
91+
override this.ToString () = $"{this.ty1.DebugText}-{this.ty2.DebugText}"
9092

91-
combined
93+
let typeSubsumptionCache =
94+
// Leave most of the capacity in reserve for bursts.
95+
lazy Cache.Create<TTypeCacheKey, bool>({ TotalCapacity = 131072; HeadroomPercentage = 75 }, name = "TypeSubsumptionCache")
9296

9397
//-------------------------------------------------------------------------
9498
// Import an IL types as F# types.
@@ -106,15 +110,13 @@ type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
106110
type ImportMap(g: TcGlobals, assemblyLoader: AssemblyLoader) =
107111
let typeRefToTyconRefCache = ConcurrentDictionary<ILTypeRef, TyconRef>()
108112

109-
let typeSubsumptionCache = ConcurrentDictionary<TTypeCacheKey, bool>(System.Environment.ProcessorCount, 1024)
110-
111113
member _.g = g
112114

113115
member _.assemblyLoader = assemblyLoader
114116

115117
member _.ILTypeRefToTyconRefCache = typeRefToTyconRefCache
116118

117-
member _.TypeSubsumptionCache = typeSubsumptionCache
119+
member val TypeSubsumptionCache: Cache<TTypeCacheKey, bool> = typeSubsumptionCache.Value
118120

119121
let CanImportILScopeRef (env: ImportMap) m scoref =
120122

src/Compiler/Checking/import.fsi

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ module internal FSharp.Compiler.Import
55

66
open Internal.Utilities.Library
77
open FSharp.Compiler.AbstractIL.IL
8+
open FSharp.Compiler.Caches
89
open FSharp.Compiler.TcGlobals
910
open FSharp.Compiler.Text
1011
open FSharp.Compiler.Xml
1112
open FSharp.Compiler.TypedTree
1213

13-
open System.Collections.Concurrent
14-
1514
#if !NO_TYPEPROVIDERS
1615
open FSharp.Compiler.TypeProviders
1716
#endif
@@ -45,15 +44,14 @@ type CanCoerce =
4544
[<Struct; NoComparison; CustomEquality>]
4645
type TTypeCacheKey =
4746
interface System.IEquatable<TTypeCacheKey>
48-
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce * tcGlobals: TcGlobals -> TTypeCacheKey
47+
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
4948

50-
static member FromStrippedTypes:
51-
ty1: TType * ty2: TType * canCoerce: CanCoerce * tcGlobals: TcGlobals -> TTypeCacheKey
49+
static member FromStrippedTypes: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey
5250

5351
val ty1: TType
5452
val ty2: TType
5553
val canCoerce: CanCoerce
56-
val tcGlobals: TcGlobals
54+
5755
override GetHashCode: unit -> int
5856

5957
/// Represents a context used for converting AbstractIL .NET and provided types to F# internal compiler data structures.
@@ -73,7 +71,7 @@ type ImportMap =
7371
member g: TcGlobals
7472

7573
/// Type subsumption cache
76-
member TypeSubsumptionCache: ConcurrentDictionary<TTypeCacheKey, bool>
74+
member TypeSubsumptionCache: Cache<TTypeCacheKey, bool>
7775

7876
module Nullness =
7977

src/Compiler/FSharp.Compiler.Service.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@
146146
<Compile Include="Utilities\lib.fsi" />
147147
<Compile Include="Utilities\lib.fs" />
148148
<Compile Include="Utilities\DependencyGraph.fs" />
149+
<Compile Include="Utilities\Caches.fsi" />
150+
<Compile Include="Utilities\Caches.fs" />
149151
<Compile Include="Utilities\LruCache.fsi" />
150152
<Compile Include="Utilities\LruCache.fs" />
151153
<Compile Include="Utilities\ImmutableArray.fsi" />

0 commit comments

Comments
 (0)