Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions azure-pipelines-PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,11 @@ stages:
_configuration: Release
_testKind: testFSharpQA
transparentCompiler:
FSHARP_CACHE_OVERRIDE: 256
vs_release:
_configuration: Release
_testKind: testVs
setupVsHive: true
transparentCompiler:
FSHARP_CACHE_OVERRIDE: 256
transparent_compiler_release:
_configuration: Release
_testKind: testCoreclr
Expand Down
31 changes: 23 additions & 8 deletions src/Compiler/Checking/TypeRelations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module internal FSharp.Compiler.TypeRelations
open FSharp.Compiler.Features
open Internal.Utilities.Collections
open Internal.Utilities.Library
open Internal.Utilities.TypeHashing.StructuralUtilities

open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.TcGlobals
Expand All @@ -19,6 +20,26 @@ open Import

#nowarn "3391"

[<Struct; NoComparison>]
type CanCoerce =
| CanCoerce
| NoCoerce

[<Struct; NoComparison>]
type TTypeCacheKey =
| TTypeCacheKey of TypeStructure * TypeStructure * CanCoerce
static member FromStrippedTypes(ty1, ty2, canCoerce) =
TTypeCacheKey(getTypeStructure ty1, getTypeStructure ty2, canCoerce)

let getTypeSubsumptionCache =
let factory (g: TcGlobals) =
let options =
match g.compilationMode with
| CompilationMode.OneOff -> Caches.CacheOptions.getDefault() |> Caches.CacheOptions.withNoEviction
| _ -> { Caches.CacheOptions.getDefault() with TotalCapacity = 65536; HeadroomPercentage = 75 }
new Caches.Cache<TTypeCacheKey, bool>(options, "typeSubsumptionCache")
Extras.WeakMap.getOrCreate factory

/// Implements a :> b without coercion based on finalized (no type variable) types
// Note: This relation is approximate and not part of the language specification.
//
Expand Down Expand Up @@ -136,14 +157,8 @@ let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1:
List.exists (TypeFeasiblySubsumesType (ndeep + 1) g amap m ty1 NoCoerce) interfaces

if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce)

match amap.TypeSubsumptionCache.TryGetValue(key) with
| true, subsumes -> subsumes
| false, _ ->
let subsumes = checkSubsumes ty1 ty2
amap.TypeSubsumptionCache.TryAdd(key, subsumes) |> ignore
subsumes
let key = TTypeCacheKey.FromStrippedTypes(ty1, ty2, canCoerce)
(getTypeSubsumptionCache g).GetOrAdd(key, fun _ -> checkSubsumes ty1 ty2)
else
checkSubsumes ty1 ty2

Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/Checking/TypeRelations.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Text
open FSharp.Compiler.TypedTree

[<Struct; NoComparison>]
type CanCoerce =
| CanCoerce
| NoCoerce

/// Implements a :> b without coercion based on finalized (no type variable) types
val TypeDefinitelySubsumesTypeNoCoercion:
ndeep: int -> g: TcGlobals -> amap: ImportMap -> m: range -> ty1: TType -> ty2: TType -> bool
Expand Down
47 changes: 0 additions & 47 deletions src/Compiler/Checking/import.fs
Original file line number Diff line number Diff line change
Expand Up @@ -50,51 +50,6 @@ type AssemblyLoader =
abstract RecordGeneratedTypeRoot : ProviderGeneratedType -> unit
#endif

[<Struct; NoComparison>]
type CanCoerce =
| CanCoerce
| NoCoerce

[<Struct; NoComparison; CustomEquality; DebuggerDisplay("{ToString()}")>]
type TTypeCacheKey =

val ty1: TType
val ty2: TType
val canCoerce: CanCoerce

private new (ty1, ty2, canCoerce) =
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce }

static member FromStrippedTypes (ty1, ty2, canCoerce) =
TTypeCacheKey(ty1, ty2, canCoerce)

interface System.IEquatable<TTypeCacheKey> with
member this.Equals other =
if this.canCoerce <> other.canCoerce then
false
elif this.ty1 === other.ty1 && this.ty2 === other.ty2 then
true
else
HashStamps.stampEquals this.ty1 other.ty1
&& HashStamps.stampEquals this.ty2 other.ty2

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

override this.GetHashCode () : int =
HashStamps.hashTType this.ty1
|> pipeToHash (HashStamps.hashTType this.ty2)
|> pipeToHash (hash this.canCoerce)

override this.ToString () = $"{this.ty1.DebugText}-{this.ty2.DebugText}"

let typeSubsumptionCache =
// Leave most of the capacity in reserve for bursts.
let options = { CacheOptions.getDefault() with TotalCapacity = 131072; HeadroomPercentage = 75 }
lazy new Cache<TTypeCacheKey, bool>(options, "typeSubsumptionCache")

//-------------------------------------------------------------------------
// Import an IL types as F# types.
//-------------------------------------------------------------------------
Expand All @@ -117,8 +72,6 @@ type ImportMap(g: TcGlobals, assemblyLoader: AssemblyLoader) =

member _.ILTypeRefToTyconRefCache = typeRefToTyconRefCache

member val TypeSubsumptionCache: Cache<TTypeCacheKey, bool> = typeSubsumptionCache.Value

let CanImportILScopeRef (env: ImportMap) m scoref =

let isResolved assemblyRef =
Expand Down
21 changes: 0 additions & 21 deletions src/Compiler/Checking/import.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,6 @@ type AssemblyLoader =
abstract RecordGeneratedTypeRoot: ProviderGeneratedType -> unit
#endif

[<Struct; NoComparison>]
type CanCoerce =
| CanCoerce
| NoCoerce

[<Struct; NoComparison; CustomEquality>]
type TTypeCacheKey =
interface System.IEquatable<TTypeCacheKey>
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey

static member FromStrippedTypes: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey

val ty1: TType
val ty2: TType
val canCoerce: CanCoerce

override GetHashCode: unit -> int

/// Represents a context used for converting AbstractIL .NET and provided types to F# internal compiler data structures.
/// Also cache the conversion of AbstractIL ILTypeRef nodes, based on hashes of these.
///
Expand All @@ -70,9 +52,6 @@ type ImportMap =
/// The TcGlobals for the import context
member g: TcGlobals

/// Type subsumption cache
member TypeSubsumptionCache: Cache<TTypeCacheKey, bool>

module Nullness =

[<Struct; NoEquality; NoComparison>]
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Service/FSharpCheckerResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ type internal TypeCheckInfo
// check that type of value is the same or subtype of tcref
// yes - allow access to protected members
// no - strip ability to access protected members
if TypeRelations.TypeFeasiblySubsumesType 0 g amap m thisTy Import.CanCoerce ty then
if TypeRelations.TypeFeasiblySubsumesType 0 g amap m thisTy TypeRelations.CanCoerce ty then
ad
else
AccessibleFrom(paths, None)
Expand Down
17 changes: 0 additions & 17 deletions src/Compiler/Utilities/Caches.fs
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,6 @@ module CacheOptions =
CacheOptions.EvictionMode = EvictionMode.NoEviction
}

module Cache =
// During testing a lot of compilations are started in app domains and subprocesses.
// This is a reliable way to pass the override to all of them.
[<Literal>]
let private overrideVariable = "FSHARP_CACHE_OVERRIDE"

/// Use for testing purposes to reduce memory consumption in testhost and its subprocesses.
let OverrideCapacityForTesting () =
Environment.SetEnvironmentVariable(overrideVariable, "4096", EnvironmentVariableTarget.Process)

let applyOverride (options: CacheOptions<_>) =
match Int32.TryParse(Environment.GetEnvironmentVariable(overrideVariable)) with
| true, n when options.TotalCapacity > n -> { options with TotalCapacity = n }
| _ -> options

// It is important that this is not a struct, because LinkedListNode holds a reference to it,
// and it holds the reference to that Node, in a circular way.
[<Sealed; NoComparison; NoEquality>]
Expand Down Expand Up @@ -176,8 +161,6 @@ type Cache<'Key, 'Value when 'Key: not null> internal (options: CacheOptions<'Ke
if options.HeadroomPercentage < 0 then
invalidArg "HeadroomPercentage" "HeadroomPercentage must be positive"

let options = Cache.applyOverride options

let name = defaultArg name (Guid.NewGuid().ToString())

// Determine evictable headroom as the percentage of total capcity, since we want to not resize the dictionary.
Expand Down
4 changes: 0 additions & 4 deletions src/Compiler/Utilities/Caches.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ module internal CacheOptions =
/// Set eviction mode to NoEviction.
val withNoEviction: CacheOptions<'Key> -> CacheOptions<'Key>

module internal Cache =
/// Use for testing purposes to reduce memory consumption in testhost and its subprocesses.
val OverrideCapacityForTesting: unit -> unit

[<Sealed; NoComparison; NoEquality>]
type internal Cache<'Key, 'Value when 'Key: not null> =
new: options: CacheOptions<'Key> * ?name: string -> Cache<'Key, 'Value>
Expand Down
Loading
Loading