From 1e750b3ec654bc87e690679398c3bacefd239210 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 12:40:43 -0700 Subject: [PATCH] Stop incremental builder from accumulating TcSymbolUses/TcResolutions/etc. (#11666) * Stop accumulating results in incremental build * ParseAndCheckProjectImpl changes * minor tweak --- src/fsharp/service/FSharpCheckerResults.fs | 57 +++++++++++++++++---- src/fsharp/service/FSharpCheckerResults.fsi | 2 +- src/fsharp/service/IncrementalBuild.fs | 54 +++++++++---------- src/fsharp/service/IncrementalBuild.fsi | 22 ++++---- src/fsharp/service/service.fs | 18 +++---- 5 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index dafefae93d3..ecfbcedfb39 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -2182,7 +2182,7 @@ type FSharpCheckProjectResults tcConfigOption: TcConfig option, keepAssemblyContents: bool, diagnostics: FSharpDiagnostic[], - details:(TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * TcSymbolUses list * + details:(TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string[] * FSharpProjectOptions) option) = @@ -2201,12 +2201,12 @@ type FSharpCheckProjectResults member _.HasCriticalErrors = details.IsNone member _.AssemblySignature = - let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() FSharpAssemblySignature(tcGlobals, thisCcu, ccuSig, tcImports, topAttribs, ccuSig) member _.TypedImplementationFiles = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, tcImports, thisCcu, _ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2215,7 +2215,7 @@ type FSharpCheckProjectResults member info.AssemblyContents = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2224,7 +2224,7 @@ type FSharpCheckProjectResults member _.GetOptimizedAssemblyContents() = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2243,10 +2243,28 @@ type FSharpCheckProjectResults // Not, this does not have to be a SyncOp, it can be called from any thread member _.GetUsesOfSymbol(symbol:FSharpSymbol, ?cancellationToken: CancellationToken) = - let (tcGlobals, _tcImports, _thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, _tcImports, _thisCcu, _ccuSig, builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + + let results = + match builderOrSymbolUses with + | Choice1Of2 builder -> + builder.SourceFiles + |> Array.ofList + |> Array.collect (fun x -> + match builder.GetCheckResultsForFileInProjectEvenIfStale x with + | Some partialCheckResults -> + match partialCheckResults.TryPeekTcInfoWithExtras() with + | Some(_, tcInfoExtras) -> + tcInfoExtras.TcSymbolUses.GetUsesOfSymbol symbol.Item + | _ -> + [||] + | _ -> + [||] + ) + | Choice2Of2 tcSymbolUses -> + tcSymbolUses.GetUsesOfSymbol symbol.Item - tcSymbolUses - |> Seq.collect (fun r -> r.GetUsesOfSymbol symbol.Item) + results |> Seq.filter (fun symbolUse -> symbolUse.ItemOccurence <> ItemOccurence.RelatedText) |> Seq.distinctBy (fun symbolUse -> symbolUse.ItemOccurence, symbolUse.Range) |> Seq.map (fun symbolUse -> @@ -2256,9 +2274,28 @@ type FSharpCheckProjectResults // Not, this does not have to be a SyncOp, it can be called from any thread member _.GetAllUsesOfAllSymbols(?cancellationToken: CancellationToken) = - let (tcGlobals, tcImports, thisCcu, ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + let (tcGlobals, tcImports, thisCcu, ccuSig, builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() let cenv = SymbolEnv(tcGlobals, thisCcu, Some ccuSig, tcImports) + let tcSymbolUses = + match builderOrSymbolUses with + | Choice1Of2 builder -> + builder.SourceFiles + |> Array.ofList + |> Array.map (fun x -> + match builder.GetCheckResultsForFileInProjectEvenIfStale x with + | Some partialCheckResults -> + match partialCheckResults.TryPeekTcInfoWithExtras() with + | Some(_, tcInfoExtras) -> + tcInfoExtras.TcSymbolUses + | _ -> + TcSymbolUses.Empty + | _ -> + TcSymbolUses.Empty + ) + | Choice2Of2 tcSymbolUses -> + [|tcSymbolUses|] + [| for r in tcSymbolUses do for symbolUseChunk in r.AllUsesOfSymbols do for symbolUse in symbolUseChunk do @@ -2353,7 +2390,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, FSharpCheckProjectResults (filename, Some tcConfig, keepAssemblyContents, errors, Some(tcGlobals, tcImports, tcFileInfo.ThisCcu, tcFileInfo.CcuSigForFile, - [tcFileInfo.ScopeSymbolUses], None, None, mkSimpleAssemblyRef "stdin", + (Choice2Of2 tcFileInfo.ScopeSymbolUses), None, None, mkSimpleAssemblyRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles, projectOptions)) diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index a179dd9d8bb..1858e4b24db 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -446,7 +446,7 @@ type public FSharpCheckProjectResults = TcImports * CcuThunk * ModuleOrNamespaceType * - TcSymbolUses list * + Choice * TopAttribs option * IRawFSharpAssemblyData option * ILAssemblyRef * diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 951c0326979..ddcfefd2a5a 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -182,14 +182,9 @@ type TcInfo = [] type TcInfoExtras = { - /// Accumulated resolutions, last file first - tcResolutionsRev: TcResolutions list - - /// Accumulated symbol uses, last file first - tcSymbolUsesRev: TcSymbolUses list - - /// Accumulated 'open' declarations, last file first - tcOpenDeclarationsRev: OpenDeclaration[] list + tcResolutions: TcResolutions + tcSymbolUses: TcSymbolUses + tcOpenDeclarations: OpenDeclaration[] /// Result of checking most recent file, if any latestImplFile: TypedImplFile option @@ -202,16 +197,16 @@ type TcInfoExtras = } member x.TcSymbolUses = - List.rev x.tcSymbolUsesRev + x.tcSymbolUses [] module TcInfoHelpers = let emptyTcInfoExtras = { - tcResolutionsRev = [] - tcSymbolUsesRev = [] - tcOpenDeclarationsRev = [] + tcResolutions = TcResolutions.Empty + tcSymbolUses = TcSymbolUses.Empty + tcOpenDeclarations = [||] latestImplFile = None itemKeyStore = None semanticClassificationKeyStore = None @@ -258,7 +253,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked: Event, fileChecked: Event, prevTcInfo: TcInfo, - prevTcInfoExtras: NodeCode, syntaxTreeOpt: SyntaxTree option, tcInfoStateOpt: TcInfoState option) as this = @@ -295,8 +289,7 @@ type BoundModel private (tcConfig: TcConfig, let defaultTypeCheck () = node { - let! prevTcInfoExtras = prevTcInfoExtras - return FullState(prevTcInfo, prevTcInfoExtras) + return PartialState(prevTcInfo) } member _.TcConfig = tcConfig @@ -347,7 +340,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked, fileChecked, prevTcInfo, - prevTcInfoExtras, newSyntaxTreeOpt, newTcInfoStateOpt) else @@ -366,7 +358,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked, fileChecked, tcInfo, - this.GetOrComputeTcInfoExtras(), Some syntaxTree, None) @@ -402,7 +393,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked, fileChecked, prevTcInfo, - prevTcInfoExtras, syntaxTreeOpt, Some finishState) } @@ -417,6 +407,13 @@ type BoundModel private (tcConfig: TcConfig, | ValueSome(tcInfo, _) -> Some tcInfo | _ -> None + member _.TryPeekTcInfoWithExtras() = + match tcInfoNode with + | TcInfoNode(_, fullGraphNode) -> + match fullGraphNode.TryPeekValue() with + | ValueSome(tcInfo, tcInfoExtras) -> Some(tcInfo, tcInfoExtras) + | _ -> None + member _.GetOrComputeTcInfo() = match tcInfoNode with | TcInfoNode(partialGraphNode, _) -> @@ -512,7 +509,6 @@ type BoundModel private (tcConfig: TcConfig, if partialCheck then return PartialState tcInfo else - let! prevTcInfoOptional = prevTcInfoExtras // Build symbol keys let itemKeyStore, semanticClassification = if enableBackgroundItemKeyStoreAndSemanticClassification then @@ -543,9 +539,9 @@ type BoundModel private (tcConfig: TcConfig, { /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away latestImplFile = if keepAssemblyContents then implFile else None - tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: prevTcInfoOptional.tcResolutionsRev - tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: prevTcInfoOptional.tcSymbolUsesRev - tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: prevTcInfoOptional.tcOpenDeclarationsRev + tcResolutions = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) + tcSymbolUses = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) + tcOpenDeclarations = sink.GetOpenDeclarations() itemKeyStore = itemKeyStore semanticClassificationKeyStore = semanticClassification } @@ -563,7 +559,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked: Event, fileChecked: Event, prevTcInfo: TcInfo, - prevTcInfoExtras: NodeCode, syntaxTreeOpt: SyntaxTree option) = BoundModel(tcConfig, tcGlobals, tcImports, keepAssemblyContents, keepAllBackgroundResolutions, @@ -573,7 +568,6 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked, fileChecked, prevTcInfo, - prevTcInfoExtras, syntaxTreeOpt, None) @@ -650,6 +644,8 @@ type PartialCheckResults (boundModel: BoundModel, timeStamp: DateTime) = member _.TryPeekTcInfo() = boundModel.TryPeekTcInfo() + member _.TryPeekTcInfoWithExtras() = boundModel.TryPeekTcInfoWithExtras() + member _.GetOrComputeTcInfo() = boundModel.GetOrComputeTcInfo() member _.GetOrComputeTcInfoWithExtras() = boundModel.GetOrComputeTcInfoWithExtras() @@ -836,7 +832,6 @@ type IncrementalBuilder( tcDependencyFiles = basicDependencies sigNameOpt = None } - let tcInfoExtras = emptyTcInfoExtras return BoundModel.Create( tcConfig, @@ -850,7 +845,6 @@ type IncrementalBuilder( beforeFileChecked, fileChecked, tcInfo, - node { return tcInfoExtras }, None) } /// Type check all files eagerly. @@ -1191,6 +1185,14 @@ type IncrementalBuilder( | Some (boundModel, timestamp) -> Some (PartialCheckResults (boundModel, timestamp)) | _ -> None + member builder.GetCheckResultsForFileInProjectEvenIfStale filename: PartialCheckResults option = + let slotOfFile = builder.GetSlotOfFileName filename + let result = tryGetSlot currentState slotOfFile + + match result with + | Some (boundModel, timestamp) -> Some (PartialCheckResults (boundModel, timestamp)) + | _ -> None + member builder.TryGetCheckResultsBeforeFileInProject (filename) = let cache = TimeStampCache defaultTimeStamp let tmpState = computeStampedFileNames currentState cache diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 5b01dddf4e5..c8c5c26da6a 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -73,14 +73,9 @@ type internal TcInfo = [] type internal TcInfoExtras = { - /// Accumulated resolutions, last file first - tcResolutionsRev: TcResolutions list - - /// Accumulated symbol uses, last file first - tcSymbolUsesRev: TcSymbolUses list - - /// Accumulated 'open' declarations, last file first - tcOpenDeclarationsRev: OpenDeclaration[] list + tcResolutions: TcResolutions + tcSymbolUses: TcSymbolUses + tcOpenDeclarations: OpenDeclaration[] /// Result of checking most recent file, if any latestImplFile: TypedImplFile option @@ -92,7 +87,7 @@ type internal TcInfoExtras = semanticClassificationKeyStore: SemanticClassificationKeyStore option } - member TcSymbolUses: TcSymbolUses list + member TcSymbolUses: TcSymbolUses /// Represents the state in the incremental graph associated with checking a file [] @@ -108,6 +103,8 @@ type internal PartialCheckResults = member TryPeekTcInfo: unit -> TcInfo option + member TryPeekTcInfoWithExtras: unit -> (TcInfo * TcInfoExtras) option + /// Compute the "TcInfo" part of the results. If `enablePartialTypeChecking` is false then /// extras will also be available. member GetOrComputeTcInfo: unit -> NodeCode @@ -178,6 +175,13 @@ type internal IncrementalBuilder = /// This is safe for use from non-compiler threads but the objects returned must in many cases be accessed only from the compiler thread. member GetCheckResultsBeforeFileInProjectEvenIfStale: filename:string -> PartialCheckResults option + /// Get the typecheck state of a slot, without checking if it is up-to-date w.r.t. + /// the timestamps on files and referenced DLLs prior to this one. Return None if the result is not available. + /// This is a very quick operation. + /// + /// This is safe for use from non-compiler threads but the objects returned must in many cases be accessed only from the compiler thread. + member GetCheckResultsForFileInProjectEvenIfStale: filename:string -> PartialCheckResults option + /// Get the preceding typecheck state of a slot, but only if it is up-to-date w.r.t. /// the timestamps on files and referenced DLLs prior to this one. Return None if the result is not available. /// This is a relatively quick operation. diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 0644bee2298..1ed29a76651 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -698,9 +698,9 @@ type BackgroundCompiler( let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - let tcResolutionsRev = tcInfoExtras.tcResolutionsRev - let tcSymbolUsesRev = tcInfoExtras.tcSymbolUsesRev - let tcOpenDeclarationsRev = tcInfoExtras.tcOpenDeclarationsRev + let tcResolutions = tcInfoExtras.tcResolutions + let tcSymbolUses = tcInfoExtras.tcSymbolUses + let tcOpenDeclarations = tcInfoExtras.tcOpenDeclarations let latestCcuSigForFile = tcInfo.latestCcuSigForFile let tcState = tcInfo.tcState let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile @@ -730,12 +730,12 @@ type BackgroundCompiler( tcState.Ccu, tcProj.TcImports, tcEnvAtEnd.AccessRights, - List.head tcResolutionsRev, - List.head tcSymbolUsesRev, + tcResolutions, + tcSymbolUses, tcEnvAtEnd.NameEnv, loadClosure, latestImplementationFile, - List.head tcOpenDeclarationsRev) + tcOpenDeclarations) return (parseResults, typedResults) } @@ -799,9 +799,9 @@ type BackgroundCompiler( let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation - let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. + let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - let tcSymbolUses = tcInfoExtras.TcSymbolUses let topAttribs = tcInfo.topAttribs let tcState = tcInfo.tcState let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile @@ -817,7 +817,7 @@ type BackgroundCompiler( keepAssemblyContents, diagnostics, Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, - tcSymbolUses, topAttribs, tcAssemblyDataOpt, ilAssemRef, + (Choice1Of2 builder), topAttribs, tcAssemblyDataOpt, ilAssemRef, tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcDependencyFiles, options))