Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.200.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

### Added

* New flag `--reusetypecheckingresults`, for skipping recompilation in some cases
* Let `dotnet fsi --help` print a link to the documentation website. ([PR #18006](https://github.com/dotnet/fsharp/pull/18006))
* Deprecate places where `seq` can be omitted. ([Language suggestion #1033](https://github.com/fsharp/fslang-suggestions/issues/1033), [PR #17772](https://github.com/dotnet/fsharp/pull/17772))
* Support literal attribute on decimals ([PR #17769](https://github.com/dotnet/fsharp/pull/17769))
Expand Down
13 changes: 13 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@ type TypeCheckingMode =
| Sequential
| Graph

[<RequireQualifiedAccess>]
type ReuseTcResults =
| On
| Off

[<RequireQualifiedAccess>]
type TypeCheckingConfig =
{
Expand Down Expand Up @@ -651,6 +656,8 @@ type TcConfigBuilder =

mutable parallelReferenceResolution: ParallelReferenceResolution

mutable reuseTcResults: ReuseTcResults

mutable captureIdentifiersWhenParsing: bool

mutable typeCheckingConfig: TypeCheckingConfig
Expand All @@ -660,6 +667,8 @@ type TcConfigBuilder =
mutable realsig: bool

mutable compilationMode: TcGlobals.CompilationMode

mutable cmdLineArgs: string array
}

// Directories to start probing in
Expand Down Expand Up @@ -861,6 +870,7 @@ type TcConfigBuilder =
xmlDocInfoLoader = None
exiter = QuitProcessExiter
parallelReferenceResolution = ParallelReferenceResolution.Off
reuseTcResults = ReuseTcResults.Off
captureIdentifiersWhenParsing = false
typeCheckingConfig =
{
Expand All @@ -875,6 +885,7 @@ type TcConfigBuilder =
realsig = false
strictIndentation = None
compilationMode = TcGlobals.CompilationMode.Unset
cmdLineArgs = [||]
}

member tcConfigB.FxResolver =
Expand Down Expand Up @@ -1415,11 +1426,13 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
member _.xmlDocInfoLoader = data.xmlDocInfoLoader
member _.exiter = data.exiter
member _.parallelReferenceResolution = data.parallelReferenceResolution
member _.reuseTcResults = data.reuseTcResults
member _.captureIdentifiersWhenParsing = data.captureIdentifiersWhenParsing
member _.typeCheckingConfig = data.typeCheckingConfig
member _.dumpSignatureData = data.dumpSignatureData
member _.realsig = data.realsig
member _.compilationMode = data.compilationMode
member _.cmdLineArgs = data.cmdLineArgs

static member Create(builder, validate) =
use _ = UseBuildPhase BuildPhase.Parameter
Expand Down
13 changes: 13 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ type ParallelReferenceResolution =
| On
| Off

[<RequireQualifiedAccess>]
type ReuseTcResults =
| On
| Off

/// Determines the algorithm used for type-checking.
[<RequireQualifiedAccess>]
type TypeCheckingMode =
Expand Down Expand Up @@ -519,6 +524,8 @@ type TcConfigBuilder =

mutable parallelReferenceResolution: ParallelReferenceResolution

mutable reuseTcResults: ReuseTcResults

mutable captureIdentifiersWhenParsing: bool

mutable typeCheckingConfig: TypeCheckingConfig
Expand All @@ -528,6 +535,8 @@ type TcConfigBuilder =
mutable realsig: bool

mutable compilationMode: TcGlobals.CompilationMode

mutable cmdLineArgs: string array
}

static member CreateNew:
Expand Down Expand Up @@ -899,6 +908,8 @@ type TcConfig =

member parallelReferenceResolution: ParallelReferenceResolution

member reuseTcResults: ReuseTcResults

member captureIdentifiersWhenParsing: bool

member typeCheckingConfig: TypeCheckingConfig
Expand All @@ -909,6 +920,8 @@ type TcConfig =

member compilationMode: TcGlobals.CompilationMode

member cmdLineArgs: string array

/// Represents a computation to return a TcConfig. Normally this is just a constant immutable TcConfig,
/// but for F# Interactive it may be based on an underlying mutable TcConfigBuilder.
[<Sealed>]
Expand Down
8 changes: 8 additions & 0 deletions src/Compiler/Driver/CompilerOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,14 @@ let advancedFlagsFsc tcConfigB =
None,
Some(FSComp.SR.optsEmitDebugInfoInQuotations (formatOptionSwitch tcConfigB.emitDebugInfoInQuotations))
)

CompilerOption(
"reusetypecheckingresults",
tagNone,
OptionUnit(fun () -> tcConfigB.reuseTcResults <- ReuseTcResults.On),
None,
Some(FSComp.SR.optsReuseTcResults ())
)
]

// OptionBlock: Internal options (test use only)
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ open FSharp.Compiler.LexerStore
open FSharp.Compiler.Lexhelp
open FSharp.Compiler.NameResolution
open FSharp.Compiler.ParseHelpers
open FSharp.Compiler.ReuseTcResults
open FSharp.Compiler.Syntax
open FSharp.Compiler.SyntaxTrivia
open FSharp.Compiler.Syntax.PrettyNaming
Expand Down Expand Up @@ -1960,6 +1961,10 @@ let CheckMultipleInputsUsingGraphMode
partialResults, tcState)

let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) =

if tcConfig.reuseTcResults = ReuseTcResults.On then
CachingDriver(tcConfig).TryReuseTcResults(inputs)

// tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions
let results, tcState =
match tcConfig.typeCheckingConfig.Mode with
Expand Down
142 changes: 142 additions & 0 deletions src/Compiler/Driver/ReuseTcResults/CachingDriver.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
module internal FSharp.Compiler.ReuseTcResults

open System.Collections.Generic
open System.IO

open FSharp.Compiler.CompilerConfig
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.GraphChecking
open FSharp.Compiler.IO
open FSharp.Compiler.Syntax
open FSharp.Compiler.Syntax.PrettyNaming

type TcData =
{
CmdLine: string array
Graph: string array
References: string array
}

[<Sealed>]
type CachingDriver(tcConfig: TcConfig) =

let outputDir = tcConfig.outputDir |> Option.defaultValue ""
let tcDataFilePath = Path.Combine(outputDir, FSharpTcDataResourceName)

[<Literal>]
let CmdLineHeader = "CMDLINE"

[<Literal>]
let GraphHeader = "GRAPH"

[<Literal>]
let ReferencesHeader = "REFERENCES"

let writeThisTcData (tcData: TcData) =
use tcDataFile = FileSystem.OpenFileForWriteShim tcDataFilePath

let lines = ResizeArray<string>()
lines.Add $"BEGIN {CmdLineHeader}"
lines.AddRange tcData.CmdLine
lines.Add $"BEGIN {GraphHeader}"
lines.AddRange tcData.Graph
lines.Add $"BEGIN {ReferencesHeader}"
lines.AddRange tcData.References

tcDataFile.WriteAllLines lines

let readPrevTcData () =
if FileSystem.FileExistsShim tcDataFilePath then
use tcDataFile = FileSystem.OpenFileForReadShim tcDataFilePath

let cmdLine = ResizeArray<string>()
let graph = ResizeArray<string>()
let refs = ResizeArray<string>()

let mutable currentHeader = ""

tcDataFile.ReadLines()
|> Seq.iter (fun line ->
match line with
| line when line.StartsWith "BEGIN" -> currentHeader <- line.Split ' ' |> Array.last
| line ->
match currentHeader with
| CmdLineHeader -> cmdLine.Add line
| GraphHeader -> graph.Add line
| ReferencesHeader -> refs.Add line
| _ -> invalidOp "broken tc cache")

Some
{
CmdLine = cmdLine.ToArray()
Graph = graph.ToArray()
References = refs.ToArray()
}

else
None

let formatAssemblyReference (r: AssemblyReference) =
let fileName = r.Text
let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName
sprintf "%s,%i" fileName lastWriteTime.Ticks

let getThisCompilationCmdLine args = args

// maybe split into two things?
let getThisCompilationGraph inputs =
let sourceFiles =
inputs
|> Seq.toArray
|> Array.mapi (fun idx (input: ParsedInput) ->
{
Idx = idx
FileName = input.FileName
ParsedInput = input
})

let filePairs = FilePairMap sourceFiles
let graph, _ = DependencyResolution.mkGraph filePairs sourceFiles

let list = List<string>()

for KeyValue(idx, _) in graph do
let fileName = sourceFiles[idx].FileName
let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName
list.Add(sprintf "%i,%s,%i" idx fileName lastWriteTime.Ticks)

for KeyValue(idx, deps) in graph do
for depIdx in deps do
list.Add $"%i{idx} --> %i{depIdx}"

list.ToArray()

let getThisCompilationReferences = Seq.map formatAssemblyReference >> Seq.toArray

member _.TryReuseTcResults inputs =
let prevTcDataOpt = readPrevTcData ()

let thisTcData =
{
CmdLine = getThisCompilationCmdLine tcConfig.cmdLineArgs
Graph = getThisCompilationGraph inputs
References = getThisCompilationReferences tcConfig.referencedDLLs
}

match prevTcDataOpt with
| Some prevTcData ->
use _ = Activity.start Activity.Events.reuseTcResultsCachePresent []

if prevTcData = thisTcData then
use _ = Activity.start Activity.Events.reuseTcResultsCacheHit []

() // do nothing, yet
else
use _ = Activity.start Activity.Events.reuseTcResultsCacheMissed []

writeThisTcData thisTcData

| None ->
use _ = Activity.start Activity.Events.reuseTcResultsCacheAbsent []

writeThisTcData thisTcData
11 changes: 11 additions & 0 deletions src/Compiler/Driver/ReuseTcResults/CachingDriver.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module internal FSharp.Compiler.ReuseTcResults

open FSharp.Compiler.CompilerConfig
open FSharp.Compiler.Syntax

[<Sealed>]
type CachingDriver =

new: tcConfig: TcConfig -> CachingDriver

member TryReuseTcResults: inputs: ParsedInput seq -> unit
1 change: 1 addition & 0 deletions src/Compiler/Driver/fsc.fs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ let main1
)

tcConfigB.exiter <- exiter
tcConfigB.cmdLineArgs <- argv

// Preset: --optimize+ -g --tailcalls+ (see 4505)
SetOptimizeSwitch tcConfigB OptionSwitch.On
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1790,3 +1790,4 @@ featureUseTypeSubsumptionCache,"Use type conversion cache during compilation"
featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase identifiers in binding patterns"
3873,chkDeprecatePlacesWhereSeqCanBeOmitted,"This construct is deprecated. Sequence expressions should be of the form 'seq {{ ... }}'"
featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted"
optsReuseTcResults,"Reuse previous typechecking results for faster compilation"
2 changes: 2 additions & 0 deletions src/Compiler/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@
<Compile Include="Driver\GraphChecking\GraphProcessing.fsi" />
<Compile Include="Driver\GraphChecking\GraphProcessing.fs" />
<Content Include="Driver\GraphChecking\Docs.md" />
<Compile Include="Driver\ReuseTcResults\CachingDriver.fsi" />
<Compile Include="Driver\ReuseTcResults\CachingDriver.fs" />
<Compile Include="Driver\ParseAndCheckInputs.fsi" />
<Compile Include="Driver\ParseAndCheckInputs.fs" />
<Compile Include="Driver\ScriptClosure.fsi" />
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/PrettyNaming.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,8 @@ let FSharpSignatureCompressedDataResourceNameB = "FSharpSignatureCompressedDataB
let FSharpOptimizationDataResourceName2 = "FSharpOptimizationInfo."
let FSharpSignatureDataResourceName2 = "FSharpSignatureInfo."

let FSharpTcDataResourceName = "FSharpTypecheckingData"

[<Literal>]
let suffixForVariablesThatMayNotBeEliminated = "$cont"

Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/PrettyNaming.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ val internal FSharpOptimizationDataResourceName2: string

val internal FSharpSignatureDataResourceName2: string

val internal FSharpTcDataResourceName: string

val GetLongNameFromString: string -> string list

val FormatAndOtherOverloadsString: int -> string
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/Utilities/Activity.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ module internal Activity =
module Events =
let cacheHit = "cacheHit"

let reuseTcResultsCachePrefix = "reuseTcResultsCache"
let reuseTcResultsCachePresent = $"{reuseTcResultsCachePrefix}Present"
let reuseTcResultsCacheAbsent = $"{reuseTcResultsCachePrefix}Absent"
let reuseTcResultsCacheHit = $"{reuseTcResultsCachePrefix}Hit"
let reuseTcResultsCacheMissed = $"{reuseTcResultsCachePrefix}Missed"

type Diagnostics.Activity with

member this.RootId =
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/Utilities/Activity.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ module internal Activity =
module Events =
val cacheHit: string

val reuseTcResultsCachePrefix: string
val reuseTcResultsCachePresent: string
val reuseTcResultsCacheAbsent: string
val reuseTcResultsCacheHit: string
val reuseTcResultsCacheMissed: string

val startNoTags: name: string -> IDisposable

val start: name: string -> tags: (string * string) seq -> IDisposable
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading