Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proof of concept: reusing typecheck results #18354

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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: 2 additions & 0 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2781,6 +2781,8 @@ type ILTypeDef

member _.MetadataIndex = metadataIndex

member _.Flags = additionalFlags
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is expected, basic APIs will likely have to increase their surface to make them accessible to the new pickling code.


member x.With
(
?name,
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,8 @@ type internal ILSecurityDecl = ILSecurityDecl of ILSecurityAction * byte[]
/// below to construct/destruct these.
[<NoComparison; NoEquality; Struct>]
type internal ILSecurityDecls =
new: array: ILSecurityDecl[] -> ILSecurityDecls

member AsList: unit -> ILSecurityDecl list

/// Represents the efficiency-oriented storage of ILSecurityDecls in another item.
Expand Down Expand Up @@ -1207,6 +1209,8 @@ type ILMethodDef =
/// name and arity.
[<NoEquality; NoComparison; Class; Sealed>]
type ILMethodDefs =
new: f: (unit -> ILMethodDef array) -> ILMethodDefs

inherit DelayInitArrayMap<ILMethodDef, string, ILMethodDef list>

interface IEnumerable<ILMethodDef>
Expand Down Expand Up @@ -1311,6 +1315,7 @@ type ILFieldDef =
/// a form to allow efficient looking up fields by name.
[<NoEquality; NoComparison; Sealed>]
type ILFieldDefs =

member internal AsList: unit -> ILFieldDef list

member internal LookupByName: string -> ILFieldDef list
Expand Down Expand Up @@ -1613,6 +1618,7 @@ type ILTypeDef =
member Encoding: ILDefaultPInvokeEncoding
member IsKnownToBeAttribute: bool
member CanContainExtensionMethods: bool
member Flags: ILTypeDefAdditionalFlags

member internal WithAccess: ILTypeDefAccess -> ILTypeDef
member internal WithNestedAccess: ILMemberAccess -> ILTypeDef
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 @@ -445,6 +445,11 @@ type TypeCheckingMode =
| Sequential
| Graph

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

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

mutable parallelReferenceResolution: ParallelReferenceResolution

mutable reuseTcResults: ReuseTcResults

mutable captureIdentifiersWhenParsing: bool

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

mutable compilationMode: TcGlobals.CompilationMode

mutable cmdLineArgs: string array
}

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

member tcConfigB.FxResolver =
Expand Down Expand Up @@ -1413,11 +1424,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
14 changes: 14 additions & 0 deletions src/Compiler/Driver/CompilerImports.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ open FSharp.Compiler.TcGlobals
open FSharp.Compiler.BuildGraph
open FSharp.Compiler.IO
open FSharp.Compiler.Text
open FSharp.Compiler.TypedTreePickle
open FSharp.Core.CompilerServices

#if !NO_TYPEPROVIDERS
Expand Down Expand Up @@ -52,6 +53,19 @@ val IsReflectedDefinitionsResource: ILResource -> bool
val GetResourceNameAndSignatureDataFuncs:
ILResource list -> (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list

/// Pickling primitive
val PickleToResource:
inMem: bool ->
file: string ->
g: TcGlobals ->
compress: bool ->
scope: CcuThunk ->
rName: string ->
rNameB: string ->
p: ('a -> WriterState -> unit) ->
x: 'a ->
ILResource * ILResource option

/// Encode the F# interface data into a set of IL attributes and resources
val EncodeSignatureData:
tcConfig: TcConfig *
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
88 changes: 52 additions & 36 deletions src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1478,8 +1478,19 @@ let CheckClosedInputSetFinish (declaredImpls: CheckedImplFile list, tcState) =
tcState, declaredImpls, ccuContents

let CheckMultipleInputsSequential (ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, inputs) =
(tcState, inputs)
||> List.mapFold (CheckOneInputEntry(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt))
let checkOneInputEntry =
CheckOneInputEntry(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt)

let mutable state = tcState

let results =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is apparently a very dumb mechanism of collecting states. Instead, the state "deltas" should be collected and merged here.

inputs
|> List.map (fun input ->
let result, newState = checkOneInputEntry state input
state <- newState // Update state for the next iteration
result, newState)

results |> List.map fst, state, results |> List.map snd

open FSharp.Compiler.GraphChecking

Expand Down Expand Up @@ -1833,7 +1844,7 @@ let CheckMultipleInputsUsingGraphMode
TcState *
(PhasedDiagnostic -> PhasedDiagnostic) *
ParsedInput list)
: FinalFileResult list * TcState =
: FinalFileResult list * TcState * TcState list =
use cts = new CancellationTokenSource()

let sourceFiles: FileInProject array =
Expand Down Expand Up @@ -1931,40 +1942,44 @@ let CheckMultipleInputsUsingGraphMode
partialResult, state)
)

UseMultipleDiagnosticLoggers (inputs, diagnosticsLogger, Some eagerFormat) (fun inputsWithLoggers ->
// Equip loggers to locally filter w.r.t. scope pragmas in each input
let inputsWithLoggers =
inputsWithLoggers
|> List.toArray
|> Array.map (fun (input, oldLogger) ->
let logger = DiagnosticsLoggerForInput(tcConfig, input, oldLogger)
input, logger)

let processFile (node: NodeToTypeCheck) (state: State) : Finisher<NodeToTypeCheck, State, PartialResult> =
match node with
| NodeToTypeCheck.ArtificialImplFile idx ->
let parsedInput, _ = inputsWithLoggers[idx]
processArtificialImplFile node parsedInput state
| NodeToTypeCheck.PhysicalFile idx ->
let parsedInput, logger = inputsWithLoggers[idx]
processFile node (parsedInput, logger) state

let state: State = tcState, priorErrors

let partialResults, (tcState, _) =
TypeCheckingGraphProcessing.processTypeCheckingGraph nodeGraph processFile state cts.Token

let partialResults =
partialResults
// Bring back the original, index-based file order.
|> List.sortBy fst
|> List.map snd

partialResults, tcState)
let results, state =
UseMultipleDiagnosticLoggers (inputs, diagnosticsLogger, Some eagerFormat) (fun inputsWithLoggers ->
// Equip loggers to locally filter w.r.t. scope pragmas in each input
let inputsWithLoggers =
inputsWithLoggers
|> List.toArray
|> Array.map (fun (input, oldLogger) ->
let logger = DiagnosticsLoggerForInput(tcConfig, input, oldLogger)
input, logger)

let processFile (node: NodeToTypeCheck) (state: State) : Finisher<NodeToTypeCheck, State, PartialResult> =
match node with
| NodeToTypeCheck.ArtificialImplFile idx ->
let parsedInput, _ = inputsWithLoggers[idx]
processArtificialImplFile node parsedInput state
| NodeToTypeCheck.PhysicalFile idx ->
let parsedInput, logger = inputsWithLoggers[idx]
processFile node (parsedInput, logger) state

let state: State = tcState, priorErrors

let partialResults, (tcState, _) =
TypeCheckingGraphProcessing.processTypeCheckingGraph nodeGraph processFile state cts.Token

let partialResults =
partialResults
// Bring back the original, index-based file order.
|> List.sortBy fst
|> List.map snd

partialResults, tcState)

// TODO: collect states here also
results, state, []

let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs) =
// tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions
let results, tcState =
let results, lastState, tcStates =
match tcConfig.typeCheckingConfig.Mode with
| TypeCheckingMode.Graph when (not tcConfig.isInteractive && not tcConfig.compilingFSharpCore) ->
CheckMultipleInputsUsingGraphMode(
Expand All @@ -1981,10 +1996,11 @@ let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tc
| _ -> CheckMultipleInputsSequential(ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, inputs)

let (tcEnvAtEndOfLastFile, topAttrs, implFiles, _), tcState =
CheckMultipleInputsFinish(results, tcState)
CheckMultipleInputsFinish(results, lastState)

let tcState, declaredImpls, ccuContents =
CheckClosedInputSetFinish(implFiles, tcState)

tcState.Ccu.Deref.Contents <- ccuContents
tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile

tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile, tcStates
20 changes: 18 additions & 2 deletions src/Compiler/Driver/ParseAndCheckInputs.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
/// Contains logic to coordinate the parsing and checking of one or a group of files
module internal FSharp.Compiler.ParseAndCheckInputs

open System.Collections.Generic
open System.IO
open Internal.Utilities.Collections
open Internal.Utilities.Library
open FSharp.Compiler.CheckBasics
open FSharp.Compiler.CheckDeclarations
Expand Down Expand Up @@ -143,9 +145,23 @@ val ParseInputFiles:
/// applying the InternalsVisibleTo in referenced assemblies and opening 'Checked' if requested.
val GetInitialTcEnv: assemblyName: string * range * TcConfig * TcImports * TcGlobals -> TcEnv * OpenDeclaration list

type RootSigs = Zmap<QualifiedNameOfFile, ModuleOrNamespaceType>

type RootImpls = Zset<QualifiedNameOfFile>

val qnameOrder: IComparer<QualifiedNameOfFile>

/// Represents the incremental type checking state for a set of inputs
[<Sealed>]
type TcState =
{ tcsCcu: CcuThunk
tcsTcSigEnv: TcEnv
tcsTcImplEnv: TcEnv
tcsCreatesGeneratedProvidedTypes: bool
tcsRootSigs: RootSigs
tcsRootImpls: RootImpls
tcsCcuSig: ModuleOrNamespaceType
tcsImplicitOpenDeclarations: OpenDeclaration list }

/// The CcuThunk for the current assembly being checked
member Ccu: CcuThunk

Expand Down Expand Up @@ -239,7 +255,7 @@ val CheckClosedInputSet:
tcState: TcState *
eagerFormat: (PhasedDiagnostic -> PhasedDiagnostic) *
inputs: ParsedInput list ->
TcState * TopAttribs * CheckedImplFile list * TcEnv
TcState * TopAttribs * CheckedImplFile list * TcEnv * TcState list
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note, TcState list includes the first state. I just kept the first state separately here to decrease the change surface a bit for now.


/// Check a single input and finish the checking
val CheckOneInputAndFinish:
Expand Down
Loading
Loading