From 13aa2e0cc66ae4fea7a2ef24b9d23e136694074e Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 28 Sep 2020 16:21:20 -0700 Subject: [PATCH 01/34] Initial work to enable FSI optimizations in tooling --- src/fsharp/ParseAndCheckInputs.fs | 81 +++++++++++++++++++++++--- src/fsharp/ParseAndCheckInputs.fsi | 3 +- src/fsharp/service/IncrementalBuild.fs | 11 ++-- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 2021776917a..0ca764b005a 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -576,7 +576,7 @@ type TcState = member x.NextStateAfterIncrementalFragment tcEnvAtEndOfLastInput = { x with tcsTcSigEnv = tcEnvAtEndOfLastInput - tcsTcImplEnv = tcEnvAtEndOfLastInput } + tcsTcImplEnv = tcEnvAtEndOfLastInput } /// Create the initial type checking state for compiling an assembly @@ -621,7 +621,7 @@ let GetInitialTcState(m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcIm tcsCcuSig = Construct.NewEmptyModuleOrNamespaceType Namespace } /// Typecheck a single file (or interactive entry into F# Interactive) -let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput) = +let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput, quickCheck: bool) = eventually { try @@ -686,11 +686,21 @@ let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: let conditionalDefines = if tcConfig.noConditionalErasure then None else Some (tcConfig.conditionalCompilationDefines) + let hadSig = rootSigOpt.IsSome + // Typecheck the implementation file - let! topAttrs, implFile, _implFileHiddenType, tcEnvAtEnd, createsGeneratedProvidedTypes = - TypeCheckOneImplFile (tcGlobals, tcState.tcsNiceNameGen, amap, tcState.tcsCcu, checkForErrors, conditionalDefines, tcSink, tcConfig.internalTestSpanStackReferring) tcImplEnv rootSigOpt file + let typeCheckOne = + if quickCheck && hadSig then + let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(rootSigOpt.Value, ModuleOrNamespaceExpr.TMDefs [], range.Zero) + let dummyImplFile = TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap []) + + (EmptyTopAttrs, dummyImplFile, Unchecked.defaultof<_>, tcImplEnv, false) + |> Eventually.Done + else + TypeCheckOneImplFile (tcGlobals, tcState.tcsNiceNameGen, amap, tcState.tcsCcu, checkForErrors, conditionalDefines, tcSink, tcConfig.internalTestSpanStackReferring) tcImplEnv rootSigOpt file + + let! topAttrs, implFile, _implFileHiddenType, tcEnvAtEnd, createsGeneratedProvidedTypes = typeCheckOne - let hadSig = rootSigOpt.IsSome let implFileSigType = SigTypeOfImplFile implFile let rootImpls = Zset.add qualNameOfFile tcState.tcsRootImpls @@ -741,7 +751,7 @@ let TypeCheckOneInput (ctok, checkForErrors, tcConfig, tcImports, tcGlobals, pre // 'use' ensures that the warning handler is restored at the end use unwindEL = PushErrorLoggerPhaseUntilUnwind(fun oldLogger -> GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput inp, oldLogger) ) use unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.TypeCheck - TypeCheckOneInputEventually (checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, inp) + TypeCheckOneInputEventually (checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, inp, false) |> Eventually.force ctok /// Finish checking multiple files (or one interactive entry into F# Interactive) @@ -756,7 +766,7 @@ let TypeCheckMultipleInputsFinish(results, tcState: TcState) = let TypeCheckOneInputAndFinishEventually(checkForErrors, tcConfig: TcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input) = eventually { Logger.LogBlockStart LogCompilerFunctionId.CompileOps_TypeCheckOneInputAndFinishEventually - let! results, tcState = TypeCheckOneInputEventually(checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input) + let! results, tcState = TypeCheckOneInputEventually(checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input, false) let result = TypeCheckMultipleInputsFinish([results], tcState) Logger.LogBlockStop LogCompilerFunctionId.CompileOps_TypeCheckOneInputAndFinishEventually return result @@ -780,3 +790,60 @@ let TypeCheckClosedInputSet (ctok, checkForErrors, tcConfig, tcImports, tcGlobal let tcState, declaredImpls = TypeCheckClosedInputSetFinish (implFiles, tcState) tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile +let TryQuickTypeCheckOneInputEventually (tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput) : Eventually<((TcEnv * TopAttribs * TypedImplFile option * ModuleOrNamespaceType) * TcState) option> = + + eventually { + try + let! ctok = Eventually.token + RequireCompilationThread ctok // Everything here requires the compilation thread since it works on the TAST + + CheckSimulateException tcConfig + + let m = inp.Range + let amap = tcImports.GetImportMap() + match inp with + | ParsedInput.SigFile _ -> return None + + | ParsedInput.ImplFile (ParsedImplFileInput (_, _, qualNameOfFile, _, _, _, _)) -> + + // Check if we've got an interface for this fragment + let rootSigOpt = tcState.tcsRootSigs.TryFind qualNameOfFile + + // Check if we've already seen an implementation for this fragment + if Zset.contains qualNameOfFile tcState.tcsRootImpls then + errorR(Error(FSComp.SR.buildImplementationAlreadyGiven(qualNameOfFile.Text), m)) + + match rootSigOpt with + | None -> return None + | Some rootSig -> + + let tcImplEnv = tcState.tcsTcImplEnv + + let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(rootSig, ModuleOrNamespaceExpr.TMDefs [], range.Zero) + let dummyImplFile = TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap []) + let dummyImplFileSigType = SigTypeOfImplFile dummyImplFile + + let rootImpls = Zset.add qualNameOfFile tcState.tcsRootImpls + + // Only add it to the environment if it didn't have a signature + let m = qualNameOfFile.Range + + // Add the implementation as to the implementation env + let tcImplEnv = AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcImplEnv dummyImplFileSigType + + // Open the prefixPath for fsi.exe (tcImplEnv) + let tcImplEnv = + match prefixPathOpt with + | Some prefixPath -> TcOpenModuleOrNamespaceDecl tcSink tcGlobals amap m tcImplEnv (prefixPath, m) + | _ -> tcImplEnv + + let tcState = + { tcState with + tcsTcImplEnv=tcImplEnv + tcsRootImpls=rootImpls } + return Some ((tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.CcuSig), tcState) + + with e -> + errorRecovery e range0 + return None + } diff --git a/src/fsharp/ParseAndCheckInputs.fsi b/src/fsharp/ParseAndCheckInputs.fsi index ba02cb05a96..6fd5c897f82 100644 --- a/src/fsharp/ParseAndCheckInputs.fsi +++ b/src/fsharp/ParseAndCheckInputs.fsi @@ -87,7 +87,8 @@ val TypeCheckOneInputEventually : LongIdent option * NameResolution.TcResultsSink * TcState * - ParsedInput + ParsedInput * + quickCheck: bool -> Eventually<(TcEnv * TopAttribs * TypedImplFile option * ModuleOrNamespaceType) * TcState> /// Finish the checking of multiple inputs diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 4004b4e456f..55f0da0e0b6 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1408,11 +1408,12 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = TypeCheckOneInputEventually ((fun () -> hadParseErrors || errorLogger.ErrorCount > 0), - tcConfig, tcAcc.tcImports, - tcAcc.tcGlobals, - None, - TcResultsSink.WithSink sink, - tcAcc.tcState, input) + tcConfig, tcAcc.tcImports, + tcAcc.tcGlobals, + None, + TcResultsSink.WithSink sink, + tcAcc.tcState, input, + true) Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away From 236805243e8b10b4cd1343e43c79e5069e3fa3dd Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 28 Sep 2020 16:22:57 -0700 Subject: [PATCH 02/34] Remove extra type-check function --- src/fsharp/ParseAndCheckInputs.fs | 58 ------------------------------- 1 file changed, 58 deletions(-) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 0ca764b005a..1ff6fb366b7 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -789,61 +789,3 @@ let TypeCheckClosedInputSet (ctok, checkForErrors, tcConfig, tcImports, tcGlobal let (tcEnvAtEndOfLastFile, topAttrs, implFiles, _), tcState = TypeCheckMultipleInputsFinish(results, tcState) let tcState, declaredImpls = TypeCheckClosedInputSetFinish (implFiles, tcState) tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile - -let TryQuickTypeCheckOneInputEventually (tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput) : Eventually<((TcEnv * TopAttribs * TypedImplFile option * ModuleOrNamespaceType) * TcState) option> = - - eventually { - try - let! ctok = Eventually.token - RequireCompilationThread ctok // Everything here requires the compilation thread since it works on the TAST - - CheckSimulateException tcConfig - - let m = inp.Range - let amap = tcImports.GetImportMap() - match inp with - | ParsedInput.SigFile _ -> return None - - | ParsedInput.ImplFile (ParsedImplFileInput (_, _, qualNameOfFile, _, _, _, _)) -> - - // Check if we've got an interface for this fragment - let rootSigOpt = tcState.tcsRootSigs.TryFind qualNameOfFile - - // Check if we've already seen an implementation for this fragment - if Zset.contains qualNameOfFile tcState.tcsRootImpls then - errorR(Error(FSComp.SR.buildImplementationAlreadyGiven(qualNameOfFile.Text), m)) - - match rootSigOpt with - | None -> return None - | Some rootSig -> - - let tcImplEnv = tcState.tcsTcImplEnv - - let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(rootSig, ModuleOrNamespaceExpr.TMDefs [], range.Zero) - let dummyImplFile = TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap []) - let dummyImplFileSigType = SigTypeOfImplFile dummyImplFile - - let rootImpls = Zset.add qualNameOfFile tcState.tcsRootImpls - - // Only add it to the environment if it didn't have a signature - let m = qualNameOfFile.Range - - // Add the implementation as to the implementation env - let tcImplEnv = AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcImplEnv dummyImplFileSigType - - // Open the prefixPath for fsi.exe (tcImplEnv) - let tcImplEnv = - match prefixPathOpt with - | Some prefixPath -> TcOpenModuleOrNamespaceDecl tcSink tcGlobals amap m tcImplEnv (prefixPath, m) - | _ -> tcImplEnv - - let tcState = - { tcState with - tcsTcImplEnv=tcImplEnv - tcsRootImpls=rootImpls } - return Some ((tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.CcuSig), tcState) - - with e -> - errorRecovery e range0 - return None - } From 300664c6a04bceb2e3f6aefc4f7666f0b553d38f Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Sep 2020 18:27:46 -0700 Subject: [PATCH 03/34] Not working but initial work to lazily eval --- src/fsharp/CompilerConfig.fs | 9 +- src/fsharp/CompilerConfig.fsi | 2 + src/fsharp/service/IncrementalBuild.fs | 454 ++++++++++++++++-------- src/fsharp/service/IncrementalBuild.fsi | 39 +- 4 files changed, 337 insertions(+), 167 deletions(-) diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index 060d3f864cc..0a6ddb6c64b 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -246,6 +246,8 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit + abstract RunEventually: Eventually<'T> -> 'T option + type ImportedAssembly = { ILScopeRef: ILScopeRef FSharpViewOfMetadata: CcuThunk @@ -605,7 +607,12 @@ type TcConfigBuilder = #endif compilationThread = let ctok = CompilationThreadToken () - { new ICompilationThread with member __.EnqueueWork work = work ctok } + { new ICompilationThread with + member __.EnqueueWork work = work ctok + member __.RunEventually work = + work + |> Eventually.forceWhile ctok (fun _ -> true) + } pause = false alwaysCallVirt = true noDebugData = false diff --git a/src/fsharp/CompilerConfig.fsi b/src/fsharp/CompilerConfig.fsi index 00e824b4054..4a8fa9c76d6 100644 --- a/src/fsharp/CompilerConfig.fsi +++ b/src/fsharp/CompilerConfig.fsi @@ -100,6 +100,8 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit + abstract RunEventually: Eventually<'T> -> 'T option + [] type CompilerTarget = | WinExe diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 55f0da0e0b6..b00a536de15 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1025,7 +1025,7 @@ module Tc = FSharp.Compiler.TypeChecker /// Accumulated results of type checking. [] -type TypeCheckAccumulator = +type TypeCheckAccumulatorState = { tcState: TcState tcImports: TcImports tcGlobals: TcGlobals @@ -1062,6 +1062,233 @@ type TypeCheckAccumulator = /// If enabled, holds semantic classification information for Item(symbol)s in a file. semanticClassification: struct (range * SemanticClassificationType) [] } +/// Accumulated results of type checking. +and [] TypeCheckAccumulator ( + keepAssemblyContents, keepAllBackgroundResolutions, + maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked: Event, + fileChecked: Event, + tcConfig: TcConfig, + initialState: TypeCheckAccumulatorState, + prevTcAcc: TypeCheckAccumulator option, + input, + finalErrorsRev: _ option, + finalTopAttribs: _ option) as this = + + let lazyTcState: TcState option ref = ref None + let lazyTcImports: TcImports option ref = ref None + let lazyTcGlobals: TcGlobals option ref = ref None + let lazyTcConfig: TcConfig option ref = ref None + let lazyTcEnvAtEndOfFile: TcEnv option ref = ref None + let lazyTcResolutionsRev: TcResolutions list option ref = ref None + let lazyTcSymbolUsesRev: TcSymbolUses list option ref = ref None + let lazyTcOpenDeclarationsRev: OpenDeclaration[] list option ref = ref None + let lazyTopAttribs: TopAttribs option option ref = ref None + let lazyLatestImplFile: TypedImplFile option option ref = ref None + let lazyLatestCcuSigForFile: ModuleOrNamespaceType option option ref = ref None + let lazyTcDependencyFiles: string list option ref = ref None + let lazyTcModuleNamesDict: ModuleNamesDict option ref = ref None + let lazyTcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list option ref = ref None + let lazyItemKeyStore: ItemKeyStore option option ref = ref None + let lazySemanticClassification: struct (range * SemanticClassificationType) [] option ref = ref None + + let mutable isFullyChecked: bool option = None + + let eval (initialItem: 'T) (lazyItem: 'T option ref) (quickCheck: bool) = + let mustCheck = + match isFullyChecked, quickCheck with + | None, _ -> + isFullyChecked <- Some(not quickCheck) + true + | Some false, true -> false + | Some false, false -> + isFullyChecked <- Some true + true + | Some true, _ -> true + + if mustCheck then + lazyItem := None + + match !lazyItem with + | Some item -> item |> Eventually.Done + | _ -> + eventually { + do! this.TypeCheck(quickCheck) + match !lazyItem with + | Some item -> return item + | _ -> return initialItem + } + + member this.Next(tcAcc: TypeCheckAccumulator, input) = + TypeCheckAccumulator( + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked, fileChecked, tcConfig, initialState, Some tcAcc, input, None, None) + + member this.Finish(finalTcErrorsRev, finalTopAttribs) = + TypeCheckAccumulator( + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked, fileChecked, tcConfig, initialState, None, input, Some finalTcErrorsRev, Some finalTopAttribs) + + member this.InitialState = initialState + + member this.tcState = + eval initialState.tcState lazyTcState true + + member _.tcImports = + eval initialState.tcImports lazyTcImports true + + member _.tcGlobals = + eval initialState.tcGlobals lazyTcGlobals true + + member _.tcConfig = + eval initialState.tcConfig lazyTcConfig true + + member _.tcEnvAtEndOfFile = + eval initialState.tcEnvAtEndOfFile lazyTcEnvAtEndOfFile true + + member _.tcResolutionsRev = + eval initialState.tcResolutionsRev lazyTcResolutionsRev false + + member _.tcSymbolUsesRev = + eval initialState.tcSymbolUsesRev lazyTcSymbolUsesRev false + + member _.tcOpenDeclarationsRev = + eval initialState.tcOpenDeclarationsRev lazyTcOpenDeclarationsRev false + + member _.topAttribs = + match finalTopAttribs with + | Some topAttribs -> Eventually.Done topAttribs + | _ -> eval initialState.topAttribs lazyTopAttribs true + + member _.latestImplFile = + eval initialState.latestImplFile lazyLatestImplFile false + + member _.latestCcuSigForFile = + eval initialState.latestCcuSigForFile lazyLatestCcuSigForFile true + + member _.tcDependencyFiles = + eval initialState.tcDependencyFiles lazyTcDependencyFiles true + + member _.tcModuleNamesDict = + eval initialState.tcModuleNamesDict lazyTcModuleNamesDict true + + member _.tcErrorsRev = + match finalErrorsRev with + | Some errorsRev -> Eventually.Done errorsRev + | _ -> eval initialState.tcErrorsRev lazyTcErrorsRev true + + member _.itemKeyStore = + eval initialState.itemKeyStore lazyItemKeyStore false + + member _.semanticClassification = + eval initialState.semanticClassification lazySemanticClassification false + + member this.TypeCheck (quickCheck: bool) = + eventually { + match prevTcAcc, input with + | Some tcAcc, (Some input, _sourceRange, filename, parseErrors) -> + IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) + let capturingErrorLogger = CompilationErrorLogger("TypeCheck", tcConfig.errorSeverityOptions) + let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput input, capturingErrorLogger) + let fullComputation = + eventually { + beforeFileChecked.Trigger filename + let! tcImports = tcAcc.tcImports + let! tcGlobals = tcAcc.tcGlobals + let! tcModuleNamesDict = tcAcc.tcModuleNamesDict + let! tcState = tcAcc.tcState + let! tcErrorsRev = tcAcc.tcErrorsRev + + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore + let sink = TcResultsSinkImpl(tcGlobals) + let hadParseErrors = not (Array.isEmpty parseErrors) + let input, moduleNamesDict = DeduplicateParsedInputModuleName tcModuleNamesDict input + + lazyTcModuleNamesDict := Some moduleNamesDict + + Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck + let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = + TypeCheckOneInputEventually + ((fun () -> hadParseErrors || errorLogger.ErrorCount > 0), + tcConfig, tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + tcState, input, + quickCheck) + Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck + + /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away + if not quickCheck then + let! tcResolutionsRev = tcAcc.tcResolutionsRev + let! tcSymbolUsesRev = tcAcc.tcSymbolUsesRev + lazyLatestImplFile := Some(if keepAssemblyContents then implFile else None) + lazyTcResolutionsRev := Some((if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev) + lazyTcEnvAtEndOfFile := Some(if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls) + lazyTcSymbolUsesRev := Some((if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev) + + // Build symbol keys + let itemKeyStore, semanticClassification = + if enableBackgroundItemKeyStoreAndSemanticClassification then + Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + let sResolutions = sink.GetResolutions() + let builder = ItemKeyStoreBuilder() + let preventDuplicates = HashSet({ new IEqualityComparer with + member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 + member _.GetHashCode o = o.GetHashCode() }) + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + if preventDuplicates.Add struct(r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcGlobals, tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) + Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + res + else + None, [||] + + lazyItemKeyStore := Some(itemKeyStore) + lazySemanticClassification := Some(semanticClassification) + + fileChecked.Trigger filename + let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) + + lazyTopAttribs := Some(Some topAttribs) + lazyLatestCcuSigForFile := Some(Some ccuSigForFile) + lazyTcErrorsRev := Some(newErrors :: tcErrorsRev) + + if not quickCheck then + let! tcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev + lazyTcOpenDeclarationsRev := Some(sink.GetOpenDeclarations() :: tcOpenDeclarationsRev) + } + + // Run part of the Eventually<_> computation until a timeout is reached. If not complete, + // return a new Eventually<_> computation which recursively runs more of the computation. + // - When the whole thing is finished commit the error results sent through the errorLogger. + // - Each time we do real work we reinstall the CompilationGlobalsScope + let timeSlicedComputation = + fullComputation |> + Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled + maxTimeShareMilliseconds + CancellationToken.None + (fun ctok f -> + // Reinstall the compilation globals each time we start or restart + use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck) + f ctok) + do! timeSlicedComputation + | _ -> + () + } /// Global service state type FrameworkImportsCacheKey = (*resolvedpath*)string list * string * (*TargetFrameworkDirectories*)string list * (*fsharpBinaries*)string * (*langVersion*)decimal @@ -1123,64 +1350,57 @@ type FrameworkImportsCache(keepStrongly) = /// Represents the interim state of checking an assembly -type PartialCheckResults = - { TcState: TcState - TcImports: TcImports - TcGlobals: TcGlobals - TcConfig: TcConfig - TcEnvAtEnd: TcEnv +[] +type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime, thread: ICompilationThread) = - /// Kept in a stack so that each incremental update shares storage with previous files - TcErrorsRev: (PhasedDiagnostic * FSharpErrorSeverity)[] list + let eval (defaultState: 'T) (work: Eventually<'T>) = + match work with + | Eventually.Done res -> res + | _ -> + match thread.RunEventually work with + | Some res -> res + | _ -> defaultState - /// Kept in a stack so that each incremental update shares storage with previous files - TcResolutionsRev: TcResolutions list + member _.TcState = tcAcc.tcState |> eval tcAcc.InitialState.tcState + member _.TcImports = tcAcc.tcImports |> eval tcAcc.InitialState.tcImports + member _.TcGlobals = tcAcc.tcGlobals |> eval tcAcc.InitialState.tcGlobals + member _.TcConfig = tcAcc.tcConfig |> eval tcAcc.InitialState.tcConfig + member _.TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile |> eval tcAcc.InitialState.tcEnvAtEndOfFile - /// Kept in a stack so that each incremental update shares storage with previous files - TcSymbolUsesRev: TcSymbolUses list + /// Kept in a stack so that each incremental update shares storage with previous files + member _.TcErrorsRev = tcAcc.tcErrorsRev |> eval tcAcc.InitialState.tcErrorsRev - /// Kept in a stack so that each incremental update shares storage with previous files - TcOpenDeclarationsRev: OpenDeclaration[] list + /// Kept in a stack so that each incremental update shares storage with previous files + member _.TcResolutionsRev = tcAcc.tcResolutionsRev |> eval tcAcc.InitialState.tcResolutionsRev - /// Disambiguation table for module names - ModuleNamesDict: ModuleNamesDict + /// Kept in a stack so that each incremental update shares storage with previous files + member _.TcSymbolUsesRev = tcAcc.tcSymbolUsesRev |> eval tcAcc.InitialState.tcSymbolUsesRev - TcDependencyFiles: string list + /// Kept in a stack so that each incremental update shares storage with previous files + member _.TcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev |> eval tcAcc.InitialState.tcOpenDeclarationsRev - TopAttribs: TopAttribs option + /// Disambiguation table for module names + member _.ModuleNamesDict = tcAcc.tcModuleNamesDict |> eval tcAcc.InitialState.tcModuleNamesDict - TimeStamp: DateTime + member _.TcDependencyFiles = tcAcc.tcDependencyFiles |> eval tcAcc.InitialState.tcDependencyFiles - LatestImplementationFile: TypedImplFile option + member _.TopAttribs = tcAcc.topAttribs |> eval tcAcc.InitialState.topAttribs - LatestCcuSigForFile: ModuleOrNamespaceType option - - ItemKeyStore: ItemKeyStore option - - SemanticClassification: struct (range * SemanticClassificationType) [] } + member _.TimeStamp = timeStamp + + member _.LatestImplementationFile = tcAcc.latestImplFile |> eval tcAcc.InitialState.latestImplFile + + member _.LatestCcuSigForFile = tcAcc.latestCcuSigForFile |> eval tcAcc.InitialState.latestCcuSigForFile + + member _.ItemKeyStore = tcAcc.itemKeyStore |> eval tcAcc.InitialState.itemKeyStore + + member _.SemanticClassification = tcAcc.semanticClassification |> eval tcAcc.InitialState.semanticClassification member x.TcErrors = Array.concat (List.rev x.TcErrorsRev) member x.TcSymbolUses = List.rev x.TcSymbolUsesRev - static member Create (tcAcc: TypeCheckAccumulator, timestamp) = - { TcState = tcAcc.tcState - TcImports = tcAcc.tcImports - TcGlobals = tcAcc.tcGlobals - TcConfig = tcAcc.tcConfig - TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile - TcErrorsRev = tcAcc.tcErrorsRev - TcResolutionsRev = tcAcc.tcResolutionsRev - TcSymbolUsesRev = tcAcc.tcSymbolUsesRev - TcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev - TcDependencyFiles = tcAcc.tcDependencyFiles - TopAttribs = tcAcc.topAttribs - ModuleNamesDict = tcAcc.tcModuleNamesDict - TimeStamp = timestamp - LatestImplementationFile = tcAcc.latestImplFile - LatestCcuSigForFile = tcAcc.latestCcuSigForFile - ItemKeyStore = tcAcc.itemKeyStore - SemanticClassification = tcAcc.semanticClassification } - + static member Create (tcAcc: TypeCheckAccumulator, timestamp, thread) = + PartialCheckResults(tcAcc, timestamp, thread) [] module Utilities = @@ -1383,102 +1603,21 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tcModuleNamesDict = Map.empty itemKeyStore = None semanticClassification = [||] } - return tcAcc } + return + TypeCheckAccumulator( + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked, fileChecked, tcConfig, tcAcc, None, (None, range0, String.Empty, [||]), None, None) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// /// Type check all files. - let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) input: Eventually = - match input with - | Some input, _sourceRange, filename, parseErrors-> - IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) - let capturingErrorLogger = CompilationErrorLogger("TypeCheckTask", tcConfig.errorSeverityOptions) - let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput input, capturingErrorLogger) - let fullComputation = - eventually { - beforeFileChecked.Trigger filename - - ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcAcc.tcImports.DependencyProvider) |> ignore - let sink = TcResultsSinkImpl(tcAcc.tcGlobals) - let hadParseErrors = not (Array.isEmpty parseErrors) - - let input, moduleNamesDict = DeduplicateParsedInputModuleName tcAcc.tcModuleNamesDict input - - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck - let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = - TypeCheckOneInputEventually - ((fun () -> hadParseErrors || errorLogger.ErrorCount > 0), - tcConfig, tcAcc.tcImports, - tcAcc.tcGlobals, - None, - TcResultsSink.WithSink sink, - tcAcc.tcState, input, - true) - Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck - - /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - let implFile = if keepAssemblyContents then implFile else None - let tcResolutions = if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty - let tcEnvAtEndOfFile = (if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls) - let tcSymbolUses = if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty - - // Build symbol keys - let itemKeyStore, semanticClassification = - if enableBackgroundItemKeyStoreAndSemanticClassification then - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - let sResolutions = sink.GetResolutions() - let builder = ItemKeyStoreBuilder() - let preventDuplicates = HashSet({ new IEqualityComparer with - member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 - member _.GetHashCode o = o.GetHashCode() }) - sResolutions.CapturedNameResolutions - |> Seq.iter (fun cnr -> - let r = cnr.Range - if preventDuplicates.Add struct(r.Start, r.End) then - builder.Write(cnr.Range, cnr.Item)) - - let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcAcc.tcGlobals, tcAcc.tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) - Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - res - else - None, [||] - - RequireCompilationThread ctok // Note: events get raised on the CompilationThread - - fileChecked.Trigger filename - let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) - return {tcAcc with tcState=tcState - tcEnvAtEndOfFile=tcEnvAtEndOfFile - topAttribs=Some topAttribs - latestImplFile=implFile - latestCcuSigForFile=Some ccuSigForFile - tcResolutionsRev=tcResolutions :: tcAcc.tcResolutionsRev - tcSymbolUsesRev=tcSymbolUses :: tcAcc.tcSymbolUsesRev - tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: tcAcc.tcOpenDeclarationsRev - tcErrorsRev = newErrors :: tcAcc.tcErrorsRev - tcModuleNamesDict = moduleNamesDict - tcDependencyFiles = filename :: tcAcc.tcDependencyFiles - itemKeyStore = itemKeyStore - semanticClassification = semanticClassification } - } - - // Run part of the Eventually<_> computation until a timeout is reached. If not complete, - // return a new Eventually<_> computation which recursively runs more of the computation. - // - When the whole thing is finished commit the error results sent through the errorLogger. - // - Each time we do real work we reinstall the CompilationGlobalsScope - let timeSlicedComputation = - fullComputation |> - Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled - maxTimeShareMilliseconds - CancellationToken.None - (fun ctok f -> - // Reinstall the compilation globals each time we start or restart - use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck) - f ctok) - - timeSlicedComputation - | _ -> - Eventually.Done tcAcc + let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) input: Eventually = + RequireCompilationThread ctok + Eventually.Done(tcAcc.Next(tcAcc, input)) /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex @@ -1494,10 +1633,21 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput // Get the state at the end of the type-checking of the last file let finalAcc = tcStates.[tcStates.Length-1] + let finalAccTcState = finalAcc.tcState |> Eventually.force ctok + let finalAccTcErrorsRev = finalAcc.tcErrorsRev |> Eventually.force ctok + // Finish the checking let (_tcEnvAtEndOfLastFile, topAttrs, mimpls, _), tcState = - let results = tcStates |> List.ofArray |> List.map (fun acc-> acc.tcEnvAtEndOfFile, defaultArg acc.topAttribs EmptyTopAttrs, acc.latestImplFile, acc.latestCcuSigForFile) - TypeCheckMultipleInputsFinish (results, finalAcc.tcState) + let results = + tcStates + |> List.ofArray + |> List.map (fun acc -> + let tcEnvAtEndOfFile = acc.tcEnvAtEndOfFile |> Eventually.force ctok + let topAttribs = acc.topAttribs |> Eventually.force ctok + let latestImplFile = acc.latestImplFile |> Eventually.force ctok + let latestCcuSigForFile = acc.latestCcuSigForFile |> Eventually.force ctok + tcEnvAtEndOfFile, defaultArg topAttribs EmptyTopAttrs, latestImplFile, latestCcuSigForFile) + TypeCheckMultipleInputsFinish (results, finalAccTcState) let ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = try @@ -1554,11 +1704,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput errorRecoveryNoRange e mkSimpleAssemblyRef assemblyName, None, None - let finalAccWithErrors = - { finalAcc with - tcErrorsRev = errorLogger.GetErrors() :: finalAcc.tcErrorsRev - topAttribs = Some topAttrs - } + let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalAccTcErrorsRev), Some topAttrs) return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalAccWithErrors } @@ -1644,7 +1790,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput | _ -> GetVectorResultBySlot(tcStatesNode, slotOfFile-1, partialBuild) match result with - | Some (tcAcc, timestamp) -> Some (PartialCheckResults.Create (tcAcc, timestamp)) + | Some (tcAcc, timestamp) -> Some (PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread)) | _ -> None @@ -1670,7 +1816,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput } match result with - | Some (tcAcc, timestamp) -> return PartialCheckResults.Create (tcAcc, timestamp) + | Some (tcAcc, timestamp) -> return PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread) | None -> return! failwith "Build was not evaluated, expected the results to be ready after 'Eval' (GetCheckResultsBeforeSlotInProject)." } @@ -1691,7 +1837,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let! build = IncrementalBuild.Eval cache ctok SavePartialBuild finalizedTypeCheckNode partialBuild match GetScalarResult(finalizedTypeCheckNode, build) with | Some ((ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, tcAcc), timestamp) -> - return PartialCheckResults.Create (tcAcc, timestamp), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt + return PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt | None -> // helpers to diagnose https://github.com/Microsoft/visualfsharp/pull/2460/ let brname = match GetTopLevelExprByName(build, finalizedTypeCheckNode.Name) with ScalarBuildRule se ->se.Id | _ -> Id 0xdeadbeef @@ -1828,6 +1974,20 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput Reactor.Singleton.EnqueueOp ("Unknown", "ICompilationThread.EnqueueWork", "work", fun ctok -> work ctok ) + member __.RunEventually work = + let workAsync = + work + |> Eventually.forceAsync + (fun work -> + Reactor.Singleton.EnqueueAndAwaitOpAsync("Unknown", "ICompilationThread.RunEventually", "work", + fun ctok -> cancellable.Return(work ctok) + )) + + let res = + workAsync + |> Async.RunSynchronously + + res } tcConfigB, sourceFilesNew diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index e7578ede1e7..51a377bf9b4 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -43,55 +43,56 @@ module internal IncrementalBuilderEventTesting = val GetCurrentIncrementalBuildEventNum : unit -> int /// Represents the state in the incremental graph associated with checking a file +[] type internal PartialCheckResults = - { + /// This field is None if a major unrecovered error occurred when preparing the initial state - TcState : TcState + member TcState : TcState - TcImports: TcImports + member TcImports: TcImports - TcGlobals: TcGlobals + member TcGlobals: TcGlobals - TcConfig: TcConfig + member TcConfig: TcConfig /// This field is None if a major unrecovered error occurred when preparing the initial state - TcEnvAtEnd : TypeChecker.TcEnv + member TcEnvAtEnd : TypeChecker.TcEnv /// Represents the collected errors from type checking - TcErrorsRev : (PhasedDiagnostic * FSharpErrorSeverity)[] list + member TcErrorsRev : (PhasedDiagnostic * FSharpErrorSeverity)[] list /// Represents the collected name resolutions from type checking - TcResolutionsRev: TcResolutions list + member TcResolutionsRev: TcResolutions list /// Represents the collected uses of symbols from type checking - TcSymbolUsesRev: TcSymbolUses list + member TcSymbolUsesRev: TcSymbolUses list /// Represents open declarations - TcOpenDeclarationsRev: OpenDeclaration[] list + member TcOpenDeclarationsRev: OpenDeclaration[] list /// Disambiguation table for module names - ModuleNamesDict: ModuleNamesDict + member ModuleNamesDict: ModuleNamesDict - TcDependencyFiles: string list + member TcDependencyFiles: string list /// Represents the collected attributes to apply to the module of assembly generates - TopAttribs: TypeChecker.TopAttribs option + member TopAttribs: TypeChecker.TopAttribs option - TimeStamp: DateTime + member TimeStamp: DateTime /// Represents latest complete typechecked implementation file, including its typechecked signature if any. /// Empty for a signature file. - LatestImplementationFile: TypedImplFile option + member LatestImplementationFile: TypedImplFile option /// Represents latest inferred signature contents. - LatestCcuSigForFile: ModuleOrNamespaceType option + member LatestCcuSigForFile: ModuleOrNamespaceType option /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. - ItemKeyStore: ItemKeyStore option + member ItemKeyStore: ItemKeyStore option /// If enabled, holds semantic classification information for Item(symbol)s in a file. - SemanticClassification: struct (range * SemanticClassificationType) [] - } + member SemanticClassification: struct (range * SemanticClassificationType) [] + member TcErrors: (PhasedDiagnostic * FSharpErrorSeverity)[] From f158eaa36c3d08a01a93ca5ad685f38d98df05a6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Sep 2020 18:45:37 -0700 Subject: [PATCH 04/34] some work --- src/fsharp/service/IncrementalBuild.fs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index b00a536de15..af1df48e999 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1129,14 +1129,15 @@ and [] TypeCheckAccumulator ( enableBackgroundItemKeyStoreAndSemanticClassification, beforeFileChecked, fileChecked, tcConfig, initialState, Some tcAcc, input, None, None) - member this.Finish(finalTcErrorsRev, finalTopAttribs) = - TypeCheckAccumulator( - keepAssemblyContents, - keepAllBackgroundResolutions, - maxTimeShareMilliseconds, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, tcConfig, initialState, None, input, Some finalTcErrorsRev, Some finalTopAttribs) + member this.Finish(_finalTcErrorsRev, _finalTopAttribs) = + this + //TypeCheckAccumulator( + // keepAssemblyContents, + // keepAllBackgroundResolutions, + // maxTimeShareMilliseconds, + // keepAllBackgroundSymbolUses, + // enableBackgroundItemKeyStoreAndSemanticClassification, + // beforeFileChecked, fileChecked, tcConfig, initialState, None, input, Some finalTcErrorsRev, Some finalTopAttribs) member this.InitialState = initialState @@ -1208,6 +1209,11 @@ and [] TypeCheckAccumulator ( let! tcState = tcAcc.tcState let! tcErrorsRev = tcAcc.tcErrorsRev + lazyTcState := Some tcState + lazyTcGlobals := Some tcGlobals + lazyTcImports := Some tcImports + lazyTcConfig := Some tcConfig + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) @@ -1266,6 +1272,7 @@ and [] TypeCheckAccumulator ( lazyTopAttribs := Some(Some topAttribs) lazyLatestCcuSigForFile := Some(Some ccuSigForFile) lazyTcErrorsRev := Some(newErrors :: tcErrorsRev) + lazyTcState := Some(tcState) if not quickCheck then let! tcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev From e83ebda50e5482fb692146827e6ed538f30ee3e7 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 04:44:27 -0700 Subject: [PATCH 05/34] simplify --- src/fsharp/service/IncrementalBuild.fs | 46 +++++++++++--------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index af1df48e999..33259604d6c 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1027,9 +1027,7 @@ module Tc = FSharp.Compiler.TypeChecker [] type TypeCheckAccumulatorState = { tcState: TcState - tcImports: TcImports - tcGlobals: TcGlobals - tcConfig: TcConfig + tcEnvAtEndOfFile: TcEnv /// Accumulated resolutions, last file first @@ -1063,13 +1061,14 @@ type TypeCheckAccumulatorState = semanticClassification: struct (range * SemanticClassificationType) [] } /// Accumulated results of type checking. -and [] TypeCheckAccumulator ( +and [] TypeCheckAccumulator (tcConfig: TcConfig, + tcGlobals: TcGlobals, + tcImports: TcImports, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, beforeFileChecked: Event, fileChecked: Event, - tcConfig: TcConfig, initialState: TypeCheckAccumulatorState, prevTcAcc: TypeCheckAccumulator option, input, @@ -1077,9 +1076,6 @@ and [] TypeCheckAccumulator ( finalTopAttribs: _ option) as this = let lazyTcState: TcState option ref = ref None - let lazyTcImports: TcImports option ref = ref None - let lazyTcGlobals: TcGlobals option ref = ref None - let lazyTcConfig: TcConfig option ref = ref None let lazyTcEnvAtEndOfFile: TcEnv option ref = ref None let lazyTcResolutionsRev: TcResolutions list option ref = ref None let lazyTcSymbolUsesRev: TcSymbolUses list option ref = ref None @@ -1122,12 +1118,15 @@ and [] TypeCheckAccumulator ( member this.Next(tcAcc: TypeCheckAccumulator, input) = TypeCheckAccumulator( + tcConfig, + tcGlobals, + tcImports, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, tcConfig, initialState, Some tcAcc, input, None, None) + beforeFileChecked, fileChecked, initialState, Some tcAcc, input, None, None) member this.Finish(_finalTcErrorsRev, _finalTopAttribs) = this @@ -1144,14 +1143,11 @@ and [] TypeCheckAccumulator ( member this.tcState = eval initialState.tcState lazyTcState true - member _.tcImports = - eval initialState.tcImports lazyTcImports true + member _.tcImports = tcImports - member _.tcGlobals = - eval initialState.tcGlobals lazyTcGlobals true + member _.tcGlobals = tcGlobals - member _.tcConfig = - eval initialState.tcConfig lazyTcConfig true + member _.tcConfig = tcConfig member _.tcEnvAtEndOfFile = eval initialState.tcEnvAtEndOfFile lazyTcEnvAtEndOfFile true @@ -1203,16 +1199,11 @@ and [] TypeCheckAccumulator ( let fullComputation = eventually { beforeFileChecked.Trigger filename - let! tcImports = tcAcc.tcImports - let! tcGlobals = tcAcc.tcGlobals let! tcModuleNamesDict = tcAcc.tcModuleNamesDict let! tcState = tcAcc.tcState let! tcErrorsRev = tcAcc.tcErrorsRev lazyTcState := Some tcState - lazyTcGlobals := Some tcGlobals - lazyTcImports := Some tcImports - lazyTcConfig := Some tcConfig ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) @@ -1369,9 +1360,9 @@ type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTi | _ -> defaultState member _.TcState = tcAcc.tcState |> eval tcAcc.InitialState.tcState - member _.TcImports = tcAcc.tcImports |> eval tcAcc.InitialState.tcImports - member _.TcGlobals = tcAcc.tcGlobals |> eval tcAcc.InitialState.tcGlobals - member _.TcConfig = tcAcc.tcConfig |> eval tcAcc.InitialState.tcConfig + member _.TcImports = tcAcc.tcImports + member _.TcGlobals = tcAcc.tcGlobals + member _.TcConfig = tcAcc.tcConfig member _.TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile |> eval tcAcc.InitialState.tcEnvAtEndOfFile /// Kept in a stack so that each incremental update shares storage with previous files @@ -1594,10 +1585,8 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let initialErrors = Array.append (Array.ofList loadClosureErrors) (errorLogger.GetErrors()) let tcAcc = - { tcGlobals=tcGlobals - tcImports=tcImports + { tcState=tcState - tcConfig=tcConfig tcEnvAtEndOfFile=tcInitial tcResolutionsRev=[] tcSymbolUsesRev=[] @@ -1612,12 +1601,15 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput semanticClassification = [||] } return TypeCheckAccumulator( + tcConfig, + tcGlobals, + tcImports, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, tcConfig, tcAcc, None, (None, range0, String.Empty, [||]), None, None) } + beforeFileChecked, fileChecked, tcAcc, None, (None, range0, String.Empty, [||]), None, None) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// From 62c0519d6a85351b68f1c8eac3c117a7c92f21a0 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 04:59:49 -0700 Subject: [PATCH 06/34] simplify again --- src/fsharp/service/IncrementalBuild.fs | 51 ++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 33259604d6c..ebb65b3c4bc 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1060,10 +1060,54 @@ type TypeCheckAccumulatorState = /// If enabled, holds semantic classification information for Item(symbol)s in a file. semanticClassification: struct (range * SemanticClassificationType) [] } +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type TypeCheckAccumulatorMinimumState = + { + tcState: TcState + tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + tcModuleNamesDict: ModuleNamesDict + + topAttribs: TopAttribs option + + latestCcuSigForFile: ModuleOrNamespaceType option + + /// Accumulated errors, last file first + tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list + } + +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type TypeCheckAccumulatorFullState = + { + tcAccMin: TypeCheckAccumulatorMinimumState + + /// 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 + + /// Result of checking most recent file, if any + latestImplFile: TypedImplFile option + + /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. + itemKeyStore: ItemKeyStore option + + /// If enabled, holds semantic classification information for Item(symbol)s in a file. + semanticClassification: struct (range * SemanticClassificationType) [] + } + /// Accumulated results of type checking. and [] TypeCheckAccumulator (tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, + tcDependencyFiles: string list, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, @@ -1121,6 +1165,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcConfig, tcGlobals, tcImports, + tcDependencyFiles, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, @@ -1203,15 +1248,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, let! tcState = tcAcc.tcState let! tcErrorsRev = tcAcc.tcErrorsRev - lazyTcState := Some tcState - ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) let input, moduleNamesDict = DeduplicateParsedInputModuleName tcModuleNamesDict input - lazyTcModuleNamesDict := Some moduleNamesDict - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = TypeCheckOneInputEventually @@ -1260,6 +1301,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, fileChecked.Trigger filename let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) + lazyTcModuleNamesDict := Some moduleNamesDict lazyTopAttribs := Some(Some topAttribs) lazyLatestCcuSigForFile := Some(Some ccuSigForFile) lazyTcErrorsRev := Some(newErrors :: tcErrorsRev) @@ -1604,6 +1646,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tcConfig, tcGlobals, tcImports, + basicDependencies, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, From 00a35bc3191414a75f2a5ddf628e45a101073f62 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 06:23:39 -0700 Subject: [PATCH 07/34] Using Async --- src/fsharp/CompilerConfig.fs | 5 +- src/fsharp/CompilerConfig.fsi | 2 +- src/fsharp/service/IncrementalBuild.fs | 435 ++++++++++++------------ src/fsharp/service/IncrementalBuild.fsi | 28 +- src/fsharp/service/service.fs | 166 +++++---- 5 files changed, 343 insertions(+), 293 deletions(-) diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index 0a6ddb6c64b..e83cb5d25c6 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -246,7 +246,7 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit - abstract RunEventually: Eventually<'T> -> 'T option + abstract RunEventually: Eventually<'T> -> Async<'T option> type ImportedAssembly = { ILScopeRef: ILScopeRef @@ -610,8 +610,7 @@ type TcConfigBuilder = { new ICompilationThread with member __.EnqueueWork work = work ctok member __.RunEventually work = - work - |> Eventually.forceWhile ctok (fun _ -> true) + Eventually.forceAsync (fun work -> async { return work ctok }) work } pause = false alwaysCallVirt = true diff --git a/src/fsharp/CompilerConfig.fsi b/src/fsharp/CompilerConfig.fsi index 4a8fa9c76d6..584af0087dd 100644 --- a/src/fsharp/CompilerConfig.fsi +++ b/src/fsharp/CompilerConfig.fsi @@ -100,7 +100,7 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit - abstract RunEventually: Eventually<'T> -> 'T option + abstract RunEventually: Eventually<'T> -> Async<'T option> [] type CompilerTarget = diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index ebb65b3c4bc..15a4b12f4eb 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1022,44 +1022,6 @@ module IncrementalBuilderEventTesting = module Tc = FSharp.Compiler.TypeChecker - -/// Accumulated results of type checking. -[] -type TypeCheckAccumulatorState = - { tcState: TcState - - tcEnvAtEndOfFile: TcEnv - - /// 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 - - topAttribs: TopAttribs option - - /// Result of checking most recent file, if any - latestImplFile: TypedImplFile option - - latestCcuSigForFile: ModuleOrNamespaceType option - - tcDependencyFiles: string list - - /// Disambiguation table for module names - tcModuleNamesDict: ModuleNamesDict - - /// Accumulated errors, last file first - tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list - - /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. - itemKeyStore: ItemKeyStore option - - /// If enabled, holds semantic classification information for Item(symbol)s in a file. - semanticClassification: struct (range * SemanticClassificationType) [] } - /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] type TypeCheckAccumulatorMinimumState = @@ -1082,7 +1044,7 @@ type TypeCheckAccumulatorMinimumState = [] type TypeCheckAccumulatorFullState = { - tcAccMin: TypeCheckAccumulatorMinimumState + tcAccMinState: TypeCheckAccumulatorMinimumState /// Accumulated resolutions, last file first tcResolutionsRev: TcResolutions list @@ -1103,6 +1065,17 @@ type TypeCheckAccumulatorFullState = semanticClassification: struct (range * SemanticClassificationType) [] } +/// Accumulated results of type checking. +[] +type TypeCheckAccumulatorState = + | MinimumState of TypeCheckAccumulatorMinimumState + | FullState of TypeCheckAccumulatorFullState + + member this.Minimum = + match this with + | MinimumState tcAccMinState -> tcAccMinState + | FullState tcAccFullState -> tcAccFullState.tcAccMinState + /// Accumulated results of type checking. and [] TypeCheckAccumulator (tcConfig: TcConfig, tcGlobals: TcGlobals, @@ -1113,65 +1086,42 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, enableBackgroundItemKeyStoreAndSemanticClassification, beforeFileChecked: Event, fileChecked: Event, - initialState: TypeCheckAccumulatorState, - prevTcAcc: TypeCheckAccumulator option, - input, - finalErrorsRev: _ option, - finalTopAttribs: _ option) as this = - - let lazyTcState: TcState option ref = ref None - let lazyTcEnvAtEndOfFile: TcEnv option ref = ref None - let lazyTcResolutionsRev: TcResolutions list option ref = ref None - let lazyTcSymbolUsesRev: TcSymbolUses list option ref = ref None - let lazyTcOpenDeclarationsRev: OpenDeclaration[] list option ref = ref None - let lazyTopAttribs: TopAttribs option option ref = ref None - let lazyLatestImplFile: TypedImplFile option option ref = ref None - let lazyLatestCcuSigForFile: ModuleOrNamespaceType option option ref = ref None - let lazyTcDependencyFiles: string list option ref = ref None - let lazyTcModuleNamesDict: ModuleNamesDict option ref = ref None - let lazyTcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list option ref = ref None - let lazyItemKeyStore: ItemKeyStore option option ref = ref None - let lazySemanticClassification: struct (range * SemanticClassificationType) [] option ref = ref None - - let mutable isFullyChecked: bool option = None - - let eval (initialItem: 'T) (lazyItem: 'T option ref) (quickCheck: bool) = - let mustCheck = - match isFullyChecked, quickCheck with - | None, _ -> - isFullyChecked <- Some(not quickCheck) - true - | Some false, true -> false - | Some false, false -> - isFullyChecked <- Some true - true - | Some true, _ -> true - - if mustCheck then - lazyItem := None - - match !lazyItem with - | Some item -> item |> Eventually.Done + prevTcMinAccState: TypeCheckAccumulatorMinimumState, + prevTcFullAccState: Lazy>, + input) as this = + + let lazyTcAccState: TypeCheckAccumulatorState option ref = ref None + + member this.GetState(quickCheck: bool) = + match !lazyTcAccState with + | Some tcAccState -> tcAccState |> Eventually.Done | _ -> eventually { - do! this.TypeCheck(quickCheck) - match !lazyItem with - | Some item -> return item - | _ -> return initialItem + let! tcAccState = this.TypeCheck(quickCheck) + lazyTcAccState := Some tcAccState + return tcAccState } - member this.Next(tcAcc: TypeCheckAccumulator, input) = - TypeCheckAccumulator( - tcConfig, - tcGlobals, - tcImports, - tcDependencyFiles, - keepAssemblyContents, - keepAllBackgroundResolutions, - maxTimeShareMilliseconds, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, initialState, Some tcAcc, input, None, None) + member this.Next(input) = + eventually { + let! state = this.GetState(false) + let fullStateOpt = + match state with + | FullState fullState -> Some fullState + | _ -> None + return + TypeCheckAccumulator( + tcConfig, + tcGlobals, + tcImports, + tcDependencyFiles, + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked, fileChecked, state.Minimum, lazy Eventually.Done(fullStateOpt), input) + } member this.Finish(_finalTcErrorsRev, _finalTopAttribs) = this @@ -1183,10 +1133,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, // enableBackgroundItemKeyStoreAndSemanticClassification, // beforeFileChecked, fileChecked, tcConfig, initialState, None, input, Some finalTcErrorsRev, Some finalTopAttribs) - member this.InitialState = initialState - member this.tcState = - eval initialState.tcState lazyTcState true + eventually { + let! state = this.GetState(true) + return state.Minimum.tcState + } member _.tcImports = tcImports @@ -1195,58 +1146,99 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member _.tcConfig = tcConfig member _.tcEnvAtEndOfFile = - eval initialState.tcEnvAtEndOfFile lazyTcEnvAtEndOfFile true + eventually { + let! state = this.GetState(true) + return state.Minimum.tcEnvAtEndOfFile + } member _.tcResolutionsRev = - eval initialState.tcResolutionsRev lazyTcResolutionsRev false + eventually { + match! this.GetState(false) with + | FullState state -> return state.tcResolutionsRev + | _ -> return [] + } member _.tcSymbolUsesRev = - eval initialState.tcSymbolUsesRev lazyTcSymbolUsesRev false + eventually { + match! this.GetState(false) with + | FullState state -> return state.tcSymbolUsesRev + | _ -> return [] + } member _.tcOpenDeclarationsRev = - eval initialState.tcOpenDeclarationsRev lazyTcOpenDeclarationsRev false + eventually { + match! this.GetState(false) with + | FullState state -> return state.tcOpenDeclarationsRev + | _ -> return [] + } member _.topAttribs = - match finalTopAttribs with - | Some topAttribs -> Eventually.Done topAttribs - | _ -> eval initialState.topAttribs lazyTopAttribs true + eventually { + let! state = this.GetState(true) + return state.Minimum.topAttribs + } member _.latestImplFile = - eval initialState.latestImplFile lazyLatestImplFile false + eventually { + match! this.GetState(false) with + | FullState state -> return state.latestImplFile + | _ -> return None + } member _.latestCcuSigForFile = - eval initialState.latestCcuSigForFile lazyLatestCcuSigForFile true + eventually { + let! state = this.GetState(true) + return state.Minimum.latestCcuSigForFile + } member _.tcDependencyFiles = - eval initialState.tcDependencyFiles lazyTcDependencyFiles true + tcDependencyFiles member _.tcModuleNamesDict = - eval initialState.tcModuleNamesDict lazyTcModuleNamesDict true + eventually { + let! state = this.GetState(true) + return state.Minimum.tcModuleNamesDict + } member _.tcErrorsRev = - match finalErrorsRev with - | Some errorsRev -> Eventually.Done errorsRev - | _ -> eval initialState.tcErrorsRev lazyTcErrorsRev true + eventually { + let! state = this.GetState(true) + return state.Minimum.tcErrorsRev + } member _.itemKeyStore = - eval initialState.itemKeyStore lazyItemKeyStore false + eventually { + match! this.GetState(false) with + | FullState state -> return state.itemKeyStore + | _ -> return None + } member _.semanticClassification = - eval initialState.semanticClassification lazySemanticClassification false + eventually { + match! this.GetState(false) with + | FullState state -> return state.semanticClassification + | _ -> return [||] + } + + member _.TypeCheck (quickCheck: bool) = + match quickCheck, !lazyTcAccState with + | true, Some (MinimumState _ as state) + | true, Some (FullState _ as state) -> state |> Eventually.Done + | false, Some (FullState _ as state) -> state |> Eventually.Done + | _ -> - member this.TypeCheck (quickCheck: bool) = eventually { - match prevTcAcc, input with - | Some tcAcc, (Some input, _sourceRange, filename, parseErrors) -> + match input with + | Some input, _sourceRange, filename, parseErrors -> IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) let capturingErrorLogger = CompilationErrorLogger("TypeCheck", tcConfig.errorSeverityOptions) let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput input, capturingErrorLogger) let fullComputation = eventually { beforeFileChecked.Trigger filename - let! tcModuleNamesDict = tcAcc.tcModuleNamesDict - let! tcState = tcAcc.tcState - let! tcErrorsRev = tcAcc.tcErrorsRev + let tcModuleNamesDict = prevTcMinAccState.tcModuleNamesDict + let tcState = prevTcMinAccState.tcState + let tcErrorsRev = prevTcMinAccState.tcErrorsRev ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) @@ -1264,52 +1256,67 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcState, input, quickCheck) Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck - - /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - if not quickCheck then - let! tcResolutionsRev = tcAcc.tcResolutionsRev - let! tcSymbolUsesRev = tcAcc.tcSymbolUsesRev - lazyLatestImplFile := Some(if keepAssemblyContents then implFile else None) - lazyTcResolutionsRev := Some((if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev) - lazyTcEnvAtEndOfFile := Some(if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls) - lazyTcSymbolUsesRev := Some((if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev) - - // Build symbol keys - let itemKeyStore, semanticClassification = - if enableBackgroundItemKeyStoreAndSemanticClassification then - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - let sResolutions = sink.GetResolutions() - let builder = ItemKeyStoreBuilder() - let preventDuplicates = HashSet({ new IEqualityComparer with - member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 - member _.GetHashCode o = o.GetHashCode() }) - sResolutions.CapturedNameResolutions - |> Seq.iter (fun cnr -> - let r = cnr.Range - if preventDuplicates.Add struct(r.Start, r.End) then - builder.Write(cnr.Range, cnr.Item)) - - let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcGlobals, tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) - Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - res - else - None, [||] - - lazyItemKeyStore := Some(itemKeyStore) - lazySemanticClassification := Some(semanticClassification) fileChecked.Trigger filename let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) - lazyTcModuleNamesDict := Some moduleNamesDict - lazyTopAttribs := Some(Some topAttribs) - lazyLatestCcuSigForFile := Some(Some ccuSigForFile) - lazyTcErrorsRev := Some(newErrors :: tcErrorsRev) - lazyTcState := Some(tcState) - - if not quickCheck then - let! tcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev - lazyTcOpenDeclarationsRev := Some(sink.GetOpenDeclarations() :: tcOpenDeclarationsRev) + let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls + + let minState = + { + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + tcModuleNamesDict = moduleNamesDict + latestCcuSigForFile = Some ccuSigForFile + tcErrorsRev = newErrors :: tcErrorsRev + topAttribs = Some topAttribs + } + + if quickCheck then + return MinimumState minState + else + match! prevTcFullAccState.Value with + | None -> return MinimumState minState + | Some fullState -> + /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away + let tcResolutionsRev = fullState.tcResolutionsRev + let tcSymbolUsesRev = fullState.tcSymbolUsesRev + let tcOpenDeclarationsRev = fullState.tcOpenDeclarationsRev + + // Build symbol keys + let itemKeyStore, semanticClassification = + if enableBackgroundItemKeyStoreAndSemanticClassification then + Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + let sResolutions = sink.GetResolutions() + let builder = ItemKeyStoreBuilder() + let preventDuplicates = HashSet({ new IEqualityComparer with + member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 + member _.GetHashCode o = o.GetHashCode() }) + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + if preventDuplicates.Add struct(r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcGlobals, tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) + Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + res + else + None, [||] + + return + { + tcAccMinState = minState + + latestImplFile = if keepAssemblyContents then implFile else None + tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev + tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev + tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: tcOpenDeclarationsRev + itemKeyStore = itemKeyStore + semanticClassification = semanticClassification + } + |> FullState + } // Run part of the Eventually<_> computation until a timeout is reached. If not complete, @@ -1325,9 +1332,9 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, // Reinstall the compilation globals each time we start or restart use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck) f ctok) - do! timeSlicedComputation + return! timeSlicedComputation | _ -> - () + return MinimumState prevTcMinAccState } /// Global service state @@ -1393,51 +1400,63 @@ type FrameworkImportsCache(keepStrongly) = [] type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime, thread: ICompilationThread) = - let eval (defaultState: 'T) (work: Eventually<'T>) = + let eval (work: Eventually<'T>) = match work with - | Eventually.Done res -> res - | _ -> - match thread.RunEventually work with - | Some res -> res - | _ -> defaultState + | Eventually.Done res -> async { return Some res } + | _ -> thread.RunEventually work - member _.TcState = tcAcc.tcState |> eval tcAcc.InitialState.tcState + member _.TcState = tcAcc.tcState |> eval member _.TcImports = tcAcc.tcImports member _.TcGlobals = tcAcc.tcGlobals member _.TcConfig = tcAcc.tcConfig - member _.TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile |> eval tcAcc.InitialState.tcEnvAtEndOfFile + member _.TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile |> eval /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcErrorsRev = tcAcc.tcErrorsRev |> eval tcAcc.InitialState.tcErrorsRev + member _.TcErrorsRev = tcAcc.tcErrorsRev |> eval /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcResolutionsRev = tcAcc.tcResolutionsRev |> eval tcAcc.InitialState.tcResolutionsRev + member _.TcResolutionsRev = tcAcc.tcResolutionsRev |> eval /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcSymbolUsesRev = tcAcc.tcSymbolUsesRev |> eval tcAcc.InitialState.tcSymbolUsesRev + member _.TcSymbolUsesRev = tcAcc.tcSymbolUsesRev |> eval /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev |> eval tcAcc.InitialState.tcOpenDeclarationsRev + member _.TcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev |> eval /// Disambiguation table for module names - member _.ModuleNamesDict = tcAcc.tcModuleNamesDict |> eval tcAcc.InitialState.tcModuleNamesDict + member _.ModuleNamesDict = tcAcc.tcModuleNamesDict |> eval - member _.TcDependencyFiles = tcAcc.tcDependencyFiles |> eval tcAcc.InitialState.tcDependencyFiles + member _.TcDependencyFiles = tcAcc.tcDependencyFiles - member _.TopAttribs = tcAcc.topAttribs |> eval tcAcc.InitialState.topAttribs + member _.TopAttribs = tcAcc.topAttribs |> eval member _.TimeStamp = timeStamp - member _.LatestImplementationFile = tcAcc.latestImplFile |> eval tcAcc.InitialState.latestImplFile + member _.LatestImplementationFile = tcAcc.latestImplFile |> eval + + member _.LatestCcuSigForFile = tcAcc.latestCcuSigForFile |> eval - member _.LatestCcuSigForFile = tcAcc.latestCcuSigForFile |> eval tcAcc.InitialState.latestCcuSigForFile + member _.ItemKeyStore = tcAcc.itemKeyStore |> eval - member _.ItemKeyStore = tcAcc.itemKeyStore |> eval tcAcc.InitialState.itemKeyStore + member _.SemanticClassification = tcAcc.semanticClassification |> eval - member _.SemanticClassification = tcAcc.semanticClassification |> eval tcAcc.InitialState.semanticClassification + member x.TcErrors = + async { + match! x.TcErrorsRev with + | Some tcErrorsRev -> + return Array.concat (List.rev tcErrorsRev) + | _ -> + return [||] + } - member x.TcErrors = Array.concat (List.rev x.TcErrorsRev) - member x.TcSymbolUses = List.rev x.TcSymbolUsesRev + member x.TcSymbolUses = + async { + match! x.TcSymbolUsesRev with + | Some tcSymbolUsesRev -> + return List.rev tcSymbolUsesRev + | _ -> + return [] + } static member Create (tcAcc: TypeCheckAccumulator, timestamp, thread) = PartialCheckResults(tcAcc, timestamp, thread) @@ -1626,21 +1645,25 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput yield err, (if isError then FSharpErrorSeverity.Error else FSharpErrorSeverity.Warning) ] let initialErrors = Array.append (Array.ofList loadClosureErrors) (errorLogger.GetErrors()) - let tcAcc = + let tcAccMinState = { tcState=tcState tcEnvAtEndOfFile=tcInitial - tcResolutionsRev=[] - tcSymbolUsesRev=[] - tcOpenDeclarationsRev=[] topAttribs=None - latestImplFile=None latestCcuSigForFile=None - tcDependencyFiles=basicDependencies tcErrorsRev = [ initialErrors ] tcModuleNamesDict = Map.empty - itemKeyStore = None - semanticClassification = [||] } + } + let tcAccFullState = + { + tcAccMinState = tcAccMinState + tcResolutionsRev=[] + tcSymbolUsesRev=[] + tcOpenDeclarationsRev=[] + latestImplFile=None + itemKeyStore = None + semanticClassification = [||] + } return TypeCheckAccumulator( tcConfig, @@ -1652,15 +1675,16 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, tcAcc, None, (None, range0, String.Empty, [||]), None, None) } + beforeFileChecked, fileChecked, tcAccMinState, lazy Eventually.Done (Some tcAccFullState), (None, range0, String.Empty, [||])) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// /// Type check all files. let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) input: Eventually = - RequireCompilationThread ctok - Eventually.Done(tcAcc.Next(tcAcc, input)) - + eventually { + RequireCompilationThread ctok + return! tcAcc.Next(input) + } /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex /// @@ -2017,19 +2041,12 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput work ctok ) member __.RunEventually work = - let workAsync = - work - |> Eventually.forceAsync - (fun work -> - Reactor.Singleton.EnqueueAndAwaitOpAsync("Unknown", "ICompilationThread.RunEventually", "work", - fun ctok -> cancellable.Return(work ctok) - )) - - let res = - workAsync - |> Async.RunSynchronously - - res + work + |> Eventually.forceAsync + (fun work -> + Reactor.Singleton.EnqueueAndAwaitOpAsync("Unknown", "ICompilationThread.RunEventually", "work", + fun ctok -> cancellable.Return(work ctok) + )) } tcConfigB, sourceFilesNew diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 51a377bf9b4..eb96cd8ef32 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -47,7 +47,7 @@ module internal IncrementalBuilderEventTesting = type internal PartialCheckResults = /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcState : TcState + member TcState : Async member TcImports: TcImports @@ -56,47 +56,47 @@ type internal PartialCheckResults = member TcConfig: TcConfig /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcEnvAtEnd : TypeChecker.TcEnv + member TcEnvAtEnd : Async /// Represents the collected errors from type checking - member TcErrorsRev : (PhasedDiagnostic * FSharpErrorSeverity)[] list + member TcErrorsRev : Async<(PhasedDiagnostic * FSharpErrorSeverity)[] list option> /// Represents the collected name resolutions from type checking - member TcResolutionsRev: TcResolutions list + member TcResolutionsRev: Async /// Represents the collected uses of symbols from type checking - member TcSymbolUsesRev: TcSymbolUses list + member TcSymbolUsesRev: Async /// Represents open declarations - member TcOpenDeclarationsRev: OpenDeclaration[] list + member TcOpenDeclarationsRev: Async /// Disambiguation table for module names - member ModuleNamesDict: ModuleNamesDict + member ModuleNamesDict: Async member TcDependencyFiles: string list /// Represents the collected attributes to apply to the module of assembly generates - member TopAttribs: TypeChecker.TopAttribs option + member TopAttribs: Async member TimeStamp: DateTime /// Represents latest complete typechecked implementation file, including its typechecked signature if any. /// Empty for a signature file. - member LatestImplementationFile: TypedImplFile option + member LatestImplementationFile: Async /// Represents latest inferred signature contents. - member LatestCcuSigForFile: ModuleOrNamespaceType option + member LatestCcuSigForFile: Async /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. - member ItemKeyStore: ItemKeyStore option + member ItemKeyStore: Async /// If enabled, holds semantic classification information for Item(symbol)s in a file. - member SemanticClassification: struct (range * SemanticClassificationType) [] + member SemanticClassification: Async - member TcErrors: (PhasedDiagnostic * FSharpErrorSeverity)[] + member TcErrors: Async<(PhasedDiagnostic * FSharpErrorSeverity)[]> - member TcSymbolUses: TcSymbolUses list + member TcSymbolUses: Async /// Manages an incremental build graph for the build of an F# project [] diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index d9b9056180e..a989486fea7 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -530,33 +530,40 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC // Get additional script #load closure information if applicable. // For scripts, this will have been recorded by GetProjectOptionsFromScript. let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) - let! checkAnswer = - FSharpCheckFileResults.CheckOneFile - (parseResults, - sourceText, - fileName, - options.ProjectFileName, - tcPrior.TcConfig, - tcPrior.TcGlobals, - tcPrior.TcImports, - tcPrior.TcState, - tcPrior.ModuleNamesDict, - loadClosure, - tcPrior.TcErrors, - reactorOps, - textSnapshotInfo, - userOpName, - options.IsIncompleteTypeCheckEnvironment, - builder, - Array.ofList tcPrior.TcDependencyFiles, - creationErrors, - parseResults.Errors, - keepAssemblyContents, - suggestNamesForErrors) - let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) - reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, sourceText.GetHashCode()) - return checkAnswer + let! tcStateOpt = tcPrior.TcState + let! moduleNamesDictOpt = tcPrior.ModuleNamesDict + match tcStateOpt, moduleNamesDictOpt with + | Some tcState, Some moduleNamesDict -> + let! tcErrors = tcPrior.TcErrors + let! checkAnswer = + FSharpCheckFileResults.CheckOneFile + (parseResults, + sourceText, + fileName, + options.ProjectFileName, + tcPrior.TcConfig, + tcPrior.TcGlobals, + tcPrior.TcImports, + tcState, + moduleNamesDict, + loadClosure, + tcErrors, + reactorOps, + textSnapshotInfo, + userOpName, + options.IsIncompleteTypeCheckEnvironment, + builder, + Array.ofList tcPrior.TcDependencyFiles, + creationErrors, + parseResults.Errors, + keepAssemblyContents, + suggestNamesForErrors) + let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) + reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang + bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, sourceText.GetHashCode()) + return checkAnswer + | _ -> + return FSharpCheckFileAnswer.Aborted finally let dummy = ref () beingCheckedFileTable.TryRemove(beingCheckedFileKey, dummy) |> ignore @@ -686,48 +693,75 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) member __.GetBackgroundCheckResultsForFileInProject(filename, options, userOpName) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> - cancellable { - let! builderOpt, creationErrors = getOrCreateBuilder (ctok, options, userOpName) - match builderOpt with - | None -> + let phase1 = + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> + cancellable { + let! builderOpt, creationErrors = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with + | None -> return creationErrors, None + | Some builder -> + let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) + let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) + return creationErrors, Some(tcProj, parseTreeOpt, untypedErrors, builder) + } + ) + + async { + let! creationErrors, resultOpt = phase1 + match resultOpt with + | None -> let parseResults = FSharpParseFileResults(creationErrors, None, true, [| |]) let typedResults = FSharpCheckFileResults.MakeEmpty(filename, creationErrors, reactorOps, keepAssemblyContents) return (parseResults, typedResults) - | Some builder -> - let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) - let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let errorOptions = builder.TcConfig.errorSeverityOptions - let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] - let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcProj.TcErrors, suggestNamesForErrors) |] - let parseResults = FSharpParseFileResults(errors = untypedErrors, input = parseTreeOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) - let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options) ) - let typedResults = - FSharpCheckFileResults.Make - (filename, - options.ProjectFileName, - tcProj.TcConfig, - tcProj.TcGlobals, - options.IsIncompleteTypeCheckEnvironment, - builder, - Array.ofList tcProj.TcDependencyFiles, - creationErrors, - parseResults.Errors, - tcErrors, - reactorOps, - keepAssemblyContents, - Option.get tcProj.LatestCcuSigForFile, - tcProj.TcState.Ccu, - tcProj.TcImports, - tcProj.TcEnvAtEnd.AccessRights, - List.head tcProj.TcResolutionsRev, - List.head tcProj.TcSymbolUsesRev, - tcProj.TcEnvAtEnd.NameEnv, - loadClosure, - tcProj.LatestImplementationFile, - List.head tcProj.TcOpenDeclarationsRev) - return (parseResults, typedResults) - }) + | Some(tcProj, parseTreeOpt, untypedErrors, builder) -> + + let! latestCcuSigForFileOpt = tcProj.LatestCcuSigForFile + let! tcStateOpt = tcProj.TcState + let! tcEnvAtEndOpt = tcProj.TcEnvAtEnd + let! tcResolutionsRevOpt = tcProj.TcResolutionsRev + let! tcSymbolUsesRevOpt = tcProj.TcSymbolUsesRev + let! tcOpenDeclarationsRevOpt = tcProj.TcOpenDeclarationsRev + let! latestImplementationFileOpt = tcProj.LatestImplementationFile + + match latestCcuSigForFileOpt, tcStateOpt, tcEnvAtEndOpt, tcResolutionsRevOpt, tcSymbolUsesRevOpt, tcOpenDeclarationsRevOpt, latestImplementationFileOpt with + | Some latestCcuSigForFile, Some tcState, Some tcEnvAtEnd, Some tcResolutionsRev, Some tcSymbolUsesRev, Some tcOpenDeclarationsRev, Some latestImplementationFile -> + let! tcErrors = tcProj.TcErrors + + let errorOptions = builder.TcConfig.errorSeverityOptions + let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] + let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcErrors, suggestNamesForErrors) |] + let parseResults = FSharpParseFileResults(errors = untypedErrors, input = parseTreeOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) + let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options) ) + let typedResults = + FSharpCheckFileResults.Make + (filename, + options.ProjectFileName, + tcProj.TcConfig, + tcProj.TcGlobals, + options.IsIncompleteTypeCheckEnvironment, + builder, + Array.ofList tcProj.TcDependencyFiles, + creationErrors, + parseResults.Errors, + tcErrors, + reactorOps, + keepAssemblyContents, + Option.get latestCcuSigForFile, + tcState.Ccu, + tcProj.TcImports, + tcEnvAtEnd.AccessRights, + List.head tcResolutionsRev, + List.head tcSymbolUsesRev, + tcEnvAtEnd.NameEnv, + loadClosure, + latestImplementationFile, + List.head tcOpenDeclarationsRev) + return (parseResults, typedResults) + | _ -> + let parseResults = FSharpParseFileResults(creationErrors, None, true, [| |]) + let typedResults = FSharpCheckFileResults.MakeEmpty(filename, creationErrors, reactorOps, keepAssemblyContents) + return (parseResults, typedResults) + } member __.FindReferencesInFile(filename: string, options: FSharpProjectOptions, symbol: FSharpSymbol, canInvalidateProject: bool, userOpName: string) = reactor.EnqueueAndAwaitOpAsync(userOpName, "FindReferencesInFile", filename, fun ctok -> From db4f0183cf25bf8cc479ccfbf27667354ee4fa24 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 06:27:34 -0700 Subject: [PATCH 08/34] work --- src/fsharp/service/service.fs | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index a989486fea7..f6900a2b126 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -764,20 +764,32 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC } member __.FindReferencesInFile(filename: string, options: FSharpProjectOptions, symbol: FSharpSymbol, canInvalidateProject: bool, userOpName: string) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "FindReferencesInFile", filename, fun ctok -> - cancellable { - let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (ctok, options, canInvalidateProject, userOpName) - match builderOpt with + let phase1 = + reactor.EnqueueAndAwaitOpAsync(userOpName, "FindReferencesInFile", filename, fun ctok -> + cancellable { + let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (ctok, options, canInvalidateProject, userOpName) + match builderOpt with + | None -> return None + | Some builder -> + if builder.ContainsFile filename then + let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) + return Some checkResults + else + return None }) + + async { + match! phase1 with | None -> return Seq.empty - | Some builder -> - if builder.ContainsFile filename then - let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return - match checkResults.ItemKeyStore with - | None -> Seq.empty - | Some reader -> reader.FindAll symbol.Item - else - return Seq.empty }) + | Some checkResults -> + let! itemKeyStoreOpt = checkResults.ItemKeyStore + match itemKeyStoreOpt with + | Some itemKeyStore -> + match itemKeyStore with + | None -> return Seq.empty + | Some reader -> return reader.FindAll symbol.Item + | _ -> + return Seq.empty + } member __.GetSemanticClassificationForFile(filename: string, options: FSharpProjectOptions, userOpName: string) = From 92c7cc853995d91af73db4586362fc1d20c9e56d Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 06:30:28 -0700 Subject: [PATCH 09/34] work --- src/fsharp/service/service.fs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index f6900a2b126..a78b06348cd 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -793,15 +793,25 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member __.GetSemanticClassificationForFile(filename: string, options: FSharpProjectOptions, userOpName: string) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "GetSemanticClassificationForFile", filename, fun ctok -> - cancellable { - let! builderOpt, _ = getOrCreateBuilder (ctok, options, userOpName) - match builderOpt with - | None -> return [||] - | Some builder -> - let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return checkResults.SemanticClassification }) + let phase1 = + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetSemanticClassificationForFile", filename, fun ctok -> + cancellable { + let! builderOpt, _ = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with + | None -> return None + | Some builder -> + let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) + return Some checkResults }) + async { + match! phase1 with + | None -> return [||] + | Some checkResults -> + let! semanticClassificationOpt = checkResults.SemanticClassification + match semanticClassificationOpt with + | Some semanticClassification -> return semanticClassification + | _ -> return [||] + } /// Try to get recent approximate type check results for a file. member __.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = From 4c3c258fd2481d8b210c1fe8f67a0b0f74517390 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 06:56:02 -0700 Subject: [PATCH 10/34] builds --- src/fsharp/service/service.fs | 61 +++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index a78b06348cd..67972499f3a 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -307,8 +307,10 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member x.EvaluateRawContents(ctok) = cancellable { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckProjectImpl", nm) - let! r = self.ParseAndCheckProjectImpl(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") - return r.RawFSharpAssemblyData + let phase1, phase2 = self.ParseAndCheckProjectImpl(opts, userOpName + ".CheckReferencedProject("+nm+")") + let! result = phase1 ctok + let result2: FSharpCheckProjectResults = phase2 result |> Async.RunSynchronously + return result2.RawFSharpAssemblyData } member x.TryGetLogicalTimeStamp(cache, ctok) = self.TryGetLogicalTimeStampForProject(cache, ctok, opts, userOpName + ".TimeStampReferencedProject("+nm+")") @@ -824,22 +826,43 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) - member private __.ParseAndCheckProjectImpl(options, ctok, userOpName) : Cancellable = - cancellable { - let! builderOpt,creationErrors = getOrCreateBuilder (ctok, options, userOpName) - match builderOpt with - | None -> - return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) - | Some builder -> - let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) + member private __.ParseAndCheckProjectImpl(options, userOpName) = + let phase1 ctok = + cancellable { + let! builderOpt,creationErrors = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with + | None -> + return creationErrors, None + | Some builder -> + let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) + return creationErrors, Some(tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) + } + + let phase2 (creationErrors, resultOpt) = + match resultOpt with + | None -> + async { return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) } + | Some(tcProj: PartialCheckResults, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) -> let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation - let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcProj.TcErrors, suggestNamesForErrors) |] - return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, - Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.CcuSig, - tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, - tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles)) - } + async { + let! tcTopAttribsOpt = tcProj.TopAttribs + let! tcStateOpt = tcProj.TcState + let! tcEnvAtEnd = tcProj.TcEnvAtEnd + match tcTopAttribsOpt, tcStateOpt, tcEnvAtEnd with + | Some topAttribs, Some tcState, Some tcEnvAtEnd -> + let! tcSymbolUses = tcProj.TcSymbolUses + let! tcErrors = tcProj.TcErrors + let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] + return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, + Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, + tcSymbolUses, topAttribs, tcAssemblyDataOpt, ilAssemRef, + tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles)) + | _ -> + return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) + } + + phase1, phase2 /// Get the timestamp that would be on the output if fully built immediately member private __.TryGetLogicalTimeStampForProject(cache, ctok, options, userOpName: string) = @@ -853,7 +876,11 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Parse and typecheck the whole project. member bc.ParseAndCheckProject(options, userOpName) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok, userOpName)) + let phase1, phase2 = bc.ParseAndCheckProjectImpl(options, userOpName) + async { + let! result = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> phase1 ctok) + return! phase2 result + } member __.GetProjectOptionsFromScript(filename, sourceText, previewEnabled, loadedTimeStamp, otherFlags, useFsiAuxLib: bool option, useSdkRefs: bool option, assumeDotNetFramework: bool option, extraProjectInfo: obj option, optionsStamp: int64 option, userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "GetProjectOptionsFromScript", filename, fun ctok -> From 46b1117d2d684ab16ea8065a5395f0ad756710e6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 08:16:47 -0700 Subject: [PATCH 11/34] almost there --- src/fsharp/service/IncrementalBuild.fs | 120 +++++---- src/fsharp/service/IncrementalBuild.fsi | 29 +- src/fsharp/service/service.fs | 334 ++++++++++-------------- 3 files changed, 227 insertions(+), 256 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 15a4b12f4eb..763f16f8f37 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1087,12 +1087,21 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, beforeFileChecked: Event, fileChecked: Event, prevTcMinAccState: TypeCheckAccumulatorMinimumState, - prevTcFullAccState: Lazy>, + prevTcFullAccState: Eventually, input) as this = let lazyTcAccState: TypeCheckAccumulatorState option ref = ref None member this.GetState(quickCheck: bool) = + let mustCheck = + match !lazyTcAccState, quickCheck with + | None, _ -> true + | Some(MinimumState _), false -> true + | _ -> false + + if mustCheck then + lazyTcAccState := None + match !lazyTcAccState with | Some tcAccState -> tcAccState |> Eventually.Done | _ -> @@ -1104,11 +1113,14 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member this.Next(input) = eventually { - let! state = this.GetState(false) - let fullStateOpt = - match state with - | FullState fullState -> Some fullState - | _ -> None + let! state = this.GetState(true) + let lazyFullState = + eventually { + let! state = this.GetState(false) + match state with + | FullState fullState -> return Some fullState + | _ -> return None + } return TypeCheckAccumulator( tcConfig, @@ -1120,18 +1132,34 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, state.Minimum, lazy Eventually.Done(fullStateOpt), input) + beforeFileChecked, fileChecked, state.Minimum, lazyFullState, input) } - member this.Finish(_finalTcErrorsRev, _finalTopAttribs) = - this - //TypeCheckAccumulator( - // keepAssemblyContents, - // keepAllBackgroundResolutions, - // maxTimeShareMilliseconds, - // keepAllBackgroundSymbolUses, - // enableBackgroundItemKeyStoreAndSemanticClassification, - // beforeFileChecked, fileChecked, tcConfig, initialState, None, input, Some finalTcErrorsRev, Some finalTopAttribs) + member this.Finish(finalTcErrorsRev, finalTopAttribs) = + eventually { + let! state = this.GetState(true) + + let lazyFullState = + eventually { + let! state = this.GetState(false) + match state with + | FullState fullState -> return Some fullState + | _ -> return None + } + + return + TypeCheckAccumulator( + tcConfig, + tcGlobals, + tcImports, + tcDependencyFiles, + keepAssemblyContents, + keepAllBackgroundResolutions, + maxTimeShareMilliseconds, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + beforeFileChecked, fileChecked, { state.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs }, lazyFullState, input) + } member this.tcState = eventually { @@ -1275,7 +1303,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, if quickCheck then return MinimumState minState else - match! prevTcFullAccState.Value with + match! prevTcFullAccState with | None -> return MinimumState minState | Some fullState -> /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away @@ -1398,65 +1426,55 @@ type FrameworkImportsCache(keepStrongly) = /// Represents the interim state of checking an assembly [] -type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime, thread: ICompilationThread) = +type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime, _thread: ICompilationThread) = - let eval (work: Eventually<'T>) = + let eval ctok (work: Eventually<'T>) = match work with - | Eventually.Done res -> async { return Some res } - | _ -> thread.RunEventually work + | Eventually.Done res -> res + | _ -> Eventually.force ctok work - member _.TcState = tcAcc.tcState |> eval + member _.TcState ctok = tcAcc.tcState |> eval ctok member _.TcImports = tcAcc.tcImports member _.TcGlobals = tcAcc.tcGlobals member _.TcConfig = tcAcc.tcConfig - member _.TcEnvAtEnd = tcAcc.tcEnvAtEndOfFile |> eval + member _.TcEnvAtEnd ctok = tcAcc.tcEnvAtEndOfFile |> eval ctok /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcErrorsRev = tcAcc.tcErrorsRev |> eval + member _.TcErrorsRev ctok = tcAcc.tcErrorsRev |> eval ctok /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcResolutionsRev = tcAcc.tcResolutionsRev |> eval + member _.TcResolutionsRev ctok = tcAcc.tcResolutionsRev |> eval ctok /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcSymbolUsesRev = tcAcc.tcSymbolUsesRev |> eval + member _.TcSymbolUsesRev ctok = tcAcc.tcSymbolUsesRev |> eval ctok /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcOpenDeclarationsRev = tcAcc.tcOpenDeclarationsRev |> eval + member _.TcOpenDeclarationsRev ctok = tcAcc.tcOpenDeclarationsRev |> eval ctok /// Disambiguation table for module names - member _.ModuleNamesDict = tcAcc.tcModuleNamesDict |> eval + member _.ModuleNamesDict ctok = tcAcc.tcModuleNamesDict |> eval ctok member _.TcDependencyFiles = tcAcc.tcDependencyFiles - member _.TopAttribs = tcAcc.topAttribs |> eval + member _.TopAttribs ctok = tcAcc.topAttribs |> eval ctok member _.TimeStamp = timeStamp - member _.LatestImplementationFile = tcAcc.latestImplFile |> eval + member _.LatestImplementationFile ctok = tcAcc.latestImplFile |> eval ctok - member _.LatestCcuSigForFile = tcAcc.latestCcuSigForFile |> eval + member _.LatestCcuSigForFile ctok = tcAcc.latestCcuSigForFile |> eval ctok - member _.ItemKeyStore = tcAcc.itemKeyStore |> eval + member _.ItemKeyStore ctok = tcAcc.itemKeyStore |> eval ctok - member _.SemanticClassification = tcAcc.semanticClassification |> eval + member _.SemanticClassification ctok = tcAcc.semanticClassification |> eval ctok - member x.TcErrors = - async { - match! x.TcErrorsRev with - | Some tcErrorsRev -> - return Array.concat (List.rev tcErrorsRev) - | _ -> - return [||] - } + member x.TcErrors ctok = + let tcErrorsRev = x.TcErrorsRev ctok + Array.concat (List.rev tcErrorsRev) - member x.TcSymbolUses = - async { - match! x.TcSymbolUsesRev with - | Some tcSymbolUsesRev -> - return List.rev tcSymbolUsesRev - | _ -> - return [] - } + member x.TcSymbolUses ctok = + let tcSymbolUsesRev = x.TcSymbolUsesRev ctok + List.rev tcSymbolUsesRev static member Create (tcAcc: TypeCheckAccumulator, timestamp, thread) = PartialCheckResults(tcAcc, timestamp, thread) @@ -1675,7 +1693,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, tcAccMinState, lazy Eventually.Done (Some tcAccFullState), (None, range0, String.Empty, [||])) } + beforeFileChecked, fileChecked, tcAccMinState, Eventually.Done (Some tcAccFullState), (None, range0, String.Empty, [||])) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// @@ -1770,7 +1788,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput errorRecoveryNoRange e mkSimpleAssemblyRef assemblyName, None, None - let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalAccTcErrorsRev), Some topAttrs) + let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalAccTcErrorsRev), Some topAttrs) |> Eventually.force ctok return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalAccWithErrors } diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index eb96cd8ef32..c1a9dd098cb 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -47,7 +47,7 @@ module internal IncrementalBuilderEventTesting = type internal PartialCheckResults = /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcState : Async + member TcState : CompilationThreadToken -> TcState member TcImports: TcImports @@ -56,47 +56,46 @@ type internal PartialCheckResults = member TcConfig: TcConfig /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcEnvAtEnd : Async + member TcEnvAtEnd : CompilationThreadToken -> TypeChecker.TcEnv /// Represents the collected errors from type checking - member TcErrorsRev : Async<(PhasedDiagnostic * FSharpErrorSeverity)[] list option> + member TcErrorsRev : CompilationThreadToken -> (PhasedDiagnostic * FSharpErrorSeverity)[] list /// Represents the collected name resolutions from type checking - member TcResolutionsRev: Async + member TcResolutionsRev: CompilationThreadToken -> TcResolutions list /// Represents the collected uses of symbols from type checking - member TcSymbolUsesRev: Async + member TcSymbolUsesRev: CompilationThreadToken -> TcSymbolUses list /// Represents open declarations - member TcOpenDeclarationsRev: Async + member TcOpenDeclarationsRev: CompilationThreadToken -> OpenDeclaration[] list /// Disambiguation table for module names - member ModuleNamesDict: Async + member ModuleNamesDict: CompilationThreadToken -> ModuleNamesDict member TcDependencyFiles: string list /// Represents the collected attributes to apply to the module of assembly generates - member TopAttribs: Async + member TopAttribs: CompilationThreadToken -> TypeChecker.TopAttribs option member TimeStamp: DateTime /// Represents latest complete typechecked implementation file, including its typechecked signature if any. /// Empty for a signature file. - member LatestImplementationFile: Async + member LatestImplementationFile: CompilationThreadToken -> TypedImplFile option /// Represents latest inferred signature contents. - member LatestCcuSigForFile: Async + member LatestCcuSigForFile: CompilationThreadToken -> ModuleOrNamespaceType option /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. - member ItemKeyStore: Async + member ItemKeyStore: CompilationThreadToken -> ItemKeyStore option /// If enabled, holds semantic classification information for Item(symbol)s in a file. - member SemanticClassification: Async - + member SemanticClassification: CompilationThreadToken -> struct (range * SemanticClassificationType) [] - member TcErrors: Async<(PhasedDiagnostic * FSharpErrorSeverity)[]> + member TcErrors: CompilationThreadToken -> (PhasedDiagnostic * FSharpErrorSeverity)[] - member TcSymbolUses: Async + member TcSymbolUses: CompilationThreadToken -> TcSymbolUses list /// Manages an incremental build graph for the build of an F# project [] diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 67972499f3a..8b6d32097a8 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -307,10 +307,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member x.EvaluateRawContents(ctok) = cancellable { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckProjectImpl", nm) - let phase1, phase2 = self.ParseAndCheckProjectImpl(opts, userOpName + ".CheckReferencedProject("+nm+")") - let! result = phase1 ctok - let result2: FSharpCheckProjectResults = phase2 result |> Async.RunSynchronously - return result2.RawFSharpAssemblyData + let! (r : FSharpCheckProjectResults) = self.ParseAndCheckProjectImpl(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") + return r.RawFSharpAssemblyData } member x.TryGetLogicalTimeStamp(cache, ctok) = self.TryGetLogicalTimeStampForProject(cache, ctok, opts, userOpName + ".TimeStampReferencedProject("+nm+")") @@ -512,7 +510,14 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC textSnapshotInfo: obj option, fileVersion: int, builder: IncrementalBuilder, - tcPrior: PartialCheckResults, + tcConfig, + tcGlobals, + tcImports, + tcDependencyFiles, + timeStamp, + prevTcState, + prevModuleNamesDict, + prevTcErrors, creationErrors: FSharpErrorInfo[], userOpName: string) = @@ -532,40 +537,33 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC // Get additional script #load closure information if applicable. // For scripts, this will have been recorded by GetProjectOptionsFromScript. let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) - let! tcStateOpt = tcPrior.TcState - let! moduleNamesDictOpt = tcPrior.ModuleNamesDict - match tcStateOpt, moduleNamesDictOpt with - | Some tcState, Some moduleNamesDict -> - let! tcErrors = tcPrior.TcErrors - let! checkAnswer = - FSharpCheckFileResults.CheckOneFile - (parseResults, - sourceText, - fileName, - options.ProjectFileName, - tcPrior.TcConfig, - tcPrior.TcGlobals, - tcPrior.TcImports, - tcState, - moduleNamesDict, - loadClosure, - tcErrors, - reactorOps, - textSnapshotInfo, - userOpName, - options.IsIncompleteTypeCheckEnvironment, - builder, - Array.ofList tcPrior.TcDependencyFiles, - creationErrors, - parseResults.Errors, - keepAssemblyContents, - suggestNamesForErrors) - let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) - reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, sourceText.GetHashCode()) - return checkAnswer - | _ -> - return FSharpCheckFileAnswer.Aborted + let! checkAnswer = + FSharpCheckFileResults.CheckOneFile + (parseResults, + sourceText, + fileName, + options.ProjectFileName, + tcConfig, + tcGlobals, + tcImports, + prevTcState, + prevModuleNamesDict, + loadClosure, + prevTcErrors, + reactorOps, + textSnapshotInfo, + userOpName, + options.IsIncompleteTypeCheckEnvironment, + builder, + Array.ofList tcDependencyFiles, + creationErrors, + parseResults.Errors, + keepAssemblyContents, + suggestNamesForErrors) + let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) + reactor.SetPreferredUILang tcConfig.preferredUiLang + bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, timeStamp, Some checkAnswer, sourceText.GetHashCode()) + return checkAnswer finally let dummy = ref () beingCheckedFileTable.TryRemove(beingCheckedFileKey, dummy) |> ignore @@ -609,12 +607,17 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC execWithReactorAsync <| fun ctok -> cancellable { DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - return builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename + let tcPrior = builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename + return + tcPrior + |> Option.map (fun tcPrior -> + (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + ) } match tcPrior with - | Some tcPrior -> - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + | Some(tcPrior, tcState, tcErrors, moduleNamesDict) -> + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) return Some checkResults | None -> return None // the incremental builder was not up to date finally @@ -639,8 +642,13 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) - let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) - let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + let! tcPrior, tcState, tcErrors, moduleNamesDict = + execWithReactorAsync <| fun ctok -> + cancellable { + let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) + return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + } + let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) return checkAnswer finally bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) @@ -677,14 +685,19 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | _ -> // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, // but results with non cooperative blocking when a firts file from a project opened. - let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) + let! tcPrior, tcState, tcErrors, moduleNamesDict = + execWithReactorAsync <| fun ctok -> + cancellable { + let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) + return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + } // Do the parsing. let parsingOptions = FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList (builder.SourceFiles), options.UseScriptResolutionRules) reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, filename, parsingOptions, userOpName, suggestNamesForErrors) let parseResults = FSharpParseFileResults(parseErrors, parseTreeOpt, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) Logger.LogBlockMessageStop (filename + strGuid + "-Successful") LogCompilerFunctionId.Service_ParseAndCheckFileInProject @@ -695,125 +708,86 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) member __.GetBackgroundCheckResultsForFileInProject(filename, options, userOpName) = - let phase1 = - reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> - cancellable { - let! builderOpt, creationErrors = getOrCreateBuilder (ctok, options, userOpName) - match builderOpt with - | None -> return creationErrors, None - | Some builder -> - let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) - let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return creationErrors, Some(tcProj, parseTreeOpt, untypedErrors, builder) - } - ) - - async { - let! creationErrors, resultOpt = phase1 - match resultOpt with + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> + cancellable { + let! builderOpt, creationErrors = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with | None -> let parseResults = FSharpParseFileResults(creationErrors, None, true, [| |]) let typedResults = FSharpCheckFileResults.MakeEmpty(filename, creationErrors, reactorOps, keepAssemblyContents) return (parseResults, typedResults) - | Some(tcProj, parseTreeOpt, untypedErrors, builder) -> - - let! latestCcuSigForFileOpt = tcProj.LatestCcuSigForFile - let! tcStateOpt = tcProj.TcState - let! tcEnvAtEndOpt = tcProj.TcEnvAtEnd - let! tcResolutionsRevOpt = tcProj.TcResolutionsRev - let! tcSymbolUsesRevOpt = tcProj.TcSymbolUsesRev - let! tcOpenDeclarationsRevOpt = tcProj.TcOpenDeclarationsRev - let! latestImplementationFileOpt = tcProj.LatestImplementationFile - - match latestCcuSigForFileOpt, tcStateOpt, tcEnvAtEndOpt, tcResolutionsRevOpt, tcSymbolUsesRevOpt, tcOpenDeclarationsRevOpt, latestImplementationFileOpt with - | Some latestCcuSigForFile, Some tcState, Some tcEnvAtEnd, Some tcResolutionsRev, Some tcSymbolUsesRev, Some tcOpenDeclarationsRev, Some latestImplementationFile -> - let! tcErrors = tcProj.TcErrors - - let errorOptions = builder.TcConfig.errorSeverityOptions - let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] - let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcErrors, suggestNamesForErrors) |] - let parseResults = FSharpParseFileResults(errors = untypedErrors, input = parseTreeOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) - let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options) ) - let typedResults = - FSharpCheckFileResults.Make - (filename, - options.ProjectFileName, - tcProj.TcConfig, - tcProj.TcGlobals, - options.IsIncompleteTypeCheckEnvironment, - builder, - Array.ofList tcProj.TcDependencyFiles, - creationErrors, - parseResults.Errors, - tcErrors, - reactorOps, - keepAssemblyContents, - Option.get latestCcuSigForFile, - tcState.Ccu, - tcProj.TcImports, - tcEnvAtEnd.AccessRights, - List.head tcResolutionsRev, - List.head tcSymbolUsesRev, - tcEnvAtEnd.NameEnv, - loadClosure, - latestImplementationFile, - List.head tcOpenDeclarationsRev) - return (parseResults, typedResults) - | _ -> - let parseResults = FSharpParseFileResults(creationErrors, None, true, [| |]) - let typedResults = FSharpCheckFileResults.MakeEmpty(filename, creationErrors, reactorOps, keepAssemblyContents) - return (parseResults, typedResults) - } + | Some builder -> + let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) + let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) + + let latestCcuSigForFile = tcProj.LatestCcuSigForFile ctok + let tcState = tcProj.TcState ctok + let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok + let tcResolutionsRev = tcProj.TcResolutionsRev ctok + let tcSymbolUsesRev = tcProj.TcSymbolUsesRev ctok + let tcOpenDeclarationsRev = tcProj.TcOpenDeclarationsRev ctok + let latestImplementationFile = tcProj.LatestImplementationFile ctok + + let tcErrors = tcProj.TcErrors ctok + + let errorOptions = builder.TcConfig.errorSeverityOptions + let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] + let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcErrors, suggestNamesForErrors) |] + let parseResults = FSharpParseFileResults(errors = untypedErrors, input = parseTreeOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) + let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options) ) + let typedResults = + FSharpCheckFileResults.Make + (filename, + options.ProjectFileName, + tcProj.TcConfig, + tcProj.TcGlobals, + options.IsIncompleteTypeCheckEnvironment, + builder, + Array.ofList tcProj.TcDependencyFiles, + creationErrors, + parseResults.Errors, + tcErrors, + reactorOps, + keepAssemblyContents, + Option.get latestCcuSigForFile, + tcState.Ccu, + tcProj.TcImports, + tcEnvAtEnd.AccessRights, + List.head tcResolutionsRev, + List.head tcSymbolUsesRev, + tcEnvAtEnd.NameEnv, + loadClosure, + latestImplementationFile, + List.head tcOpenDeclarationsRev) + return (parseResults, typedResults) + } + ) member __.FindReferencesInFile(filename: string, options: FSharpProjectOptions, symbol: FSharpSymbol, canInvalidateProject: bool, userOpName: string) = - let phase1 = - reactor.EnqueueAndAwaitOpAsync(userOpName, "FindReferencesInFile", filename, fun ctok -> - cancellable { - let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (ctok, options, canInvalidateProject, userOpName) - match builderOpt with - | None -> return None - | Some builder -> - if builder.ContainsFile filename then - let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return Some checkResults - else - return None }) - - async { - match! phase1 with + reactor.EnqueueAndAwaitOpAsync(userOpName, "FindReferencesInFile", filename, fun ctok -> + cancellable { + let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (ctok, options, canInvalidateProject, userOpName) + match builderOpt with | None -> return Seq.empty - | Some checkResults -> - let! itemKeyStoreOpt = checkResults.ItemKeyStore - match itemKeyStoreOpt with - | Some itemKeyStore -> - match itemKeyStore with + | Some builder -> + if builder.ContainsFile filename then + let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) + match checkResults.ItemKeyStore ctok with | None -> return Seq.empty | Some reader -> return reader.FindAll symbol.Item - | _ -> - return Seq.empty - } + else + return Seq.empty }) member __.GetSemanticClassificationForFile(filename: string, options: FSharpProjectOptions, userOpName: string) = - let phase1 = - reactor.EnqueueAndAwaitOpAsync(userOpName, "GetSemanticClassificationForFile", filename, fun ctok -> - cancellable { - let! builderOpt, _ = getOrCreateBuilder (ctok, options, userOpName) - match builderOpt with - | None -> return None - | Some builder -> - let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return Some checkResults }) - - async { - match! phase1 with - | None -> return [||] - | Some checkResults -> - let! semanticClassificationOpt = checkResults.SemanticClassification - match semanticClassificationOpt with - | Some semanticClassification -> return semanticClassification - | _ -> return [||] - } + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetSemanticClassificationForFile", filename, fun ctok -> + cancellable { + let! builderOpt, _ = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with + | None -> return [||] + | Some builder -> + let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) + return checkResults.SemanticClassification ctok }) /// Try to get recent approximate type check results for a file. member __.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = @@ -826,43 +800,27 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) - member private __.ParseAndCheckProjectImpl(options, userOpName) = - let phase1 ctok = - cancellable { + member private __.ParseAndCheckProjectImpl(options, ctok, userOpName) = + cancellable { let! builderOpt,creationErrors = getOrCreateBuilder (ctok, options, userOpName) match builderOpt with | None -> - return creationErrors, None + return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) | Some builder -> let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) - return creationErrors, Some(tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) - } - - let phase2 (creationErrors, resultOpt) = - match resultOpt with - | None -> - async { return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) } - | Some(tcProj: PartialCheckResults, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) -> - let errorOptions = tcProj.TcConfig.errorSeverityOptions - let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation - async { - let! tcTopAttribsOpt = tcProj.TopAttribs - let! tcStateOpt = tcProj.TcState - let! tcEnvAtEnd = tcProj.TcEnvAtEnd - match tcTopAttribsOpt, tcStateOpt, tcEnvAtEnd with - | Some topAttribs, Some tcState, Some tcEnvAtEnd -> - let! tcSymbolUses = tcProj.TcSymbolUses - let! tcErrors = tcProj.TcErrors - let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] - return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, - Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, - tcSymbolUses, topAttribs, tcAssemblyDataOpt, ilAssemRef, - tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles)) - | _ -> - return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationErrors, None) - } - - phase1, phase2 + let errorOptions = tcProj.TcConfig.errorSeverityOptions + let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation + let topAttribs = tcProj.TopAttribs ctok + let tcState = tcProj.TcState ctok + let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok + let tcSymbolUses = tcProj.TcSymbolUses ctok + let tcErrors = tcProj.TcErrors ctok + let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] + return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, + Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, + tcSymbolUses, topAttribs, tcAssemblyDataOpt, ilAssemRef, + tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles)) + } /// Get the timestamp that would be on the output if fully built immediately member private __.TryGetLogicalTimeStampForProject(cache, ctok, options, userOpName: string) = @@ -876,11 +834,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Parse and typecheck the whole project. member bc.ParseAndCheckProject(options, userOpName) = - let phase1, phase2 = bc.ParseAndCheckProjectImpl(options, userOpName) - async { - let! result = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> phase1 ctok) - return! phase2 result - } + reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok, userOpName)) member __.GetProjectOptionsFromScript(filename, sourceText, previewEnabled, loadedTimeStamp, otherFlags, useFsiAuxLib: bool option, useSdkRefs: bool option, assumeDotNetFramework: bool option, extraProjectInfo: obj option, optionsStamp: int64 option, userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "GetProjectOptionsFromScript", filename, fun ctok -> From 33481c2c84f22fa420ba2ff20cc0e3d3894d60ac Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 08:20:05 -0700 Subject: [PATCH 12/34] Minor change --- src/fsharp/service/IncrementalBuild.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 763f16f8f37..8b808426f7d 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1139,11 +1139,12 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, eventually { let! state = this.GetState(true) + let minState = { state.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs } let lazyFullState = eventually { let! state = this.GetState(false) match state with - | FullState fullState -> return Some fullState + | FullState fullState -> return Some { fullState with tcAccMinState = minState } | _ -> return None } @@ -1158,7 +1159,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, { state.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs }, lazyFullState, input) + beforeFileChecked, fileChecked, minState, lazyFullState, input) } member this.tcState = From 315be1f226feea6c83165bef1ab00ce82a29163e Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 09:24:52 -0700 Subject: [PATCH 13/34] minor refactor --- src/fsharp/service/IncrementalBuild.fs | 55 ++++++++++++-------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 8b808426f7d..6a930e59af4 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1044,8 +1044,6 @@ type TypeCheckAccumulatorMinimumState = [] type TypeCheckAccumulatorFullState = { - tcAccMinState: TypeCheckAccumulatorMinimumState - /// Accumulated resolutions, last file first tcResolutionsRev: TcResolutions list @@ -1069,12 +1067,12 @@ type TypeCheckAccumulatorFullState = [] type TypeCheckAccumulatorState = | MinimumState of TypeCheckAccumulatorMinimumState - | FullState of TypeCheckAccumulatorFullState + | FullState of TypeCheckAccumulatorMinimumState * TypeCheckAccumulatorFullState member this.Minimum = match this with | MinimumState tcAccMinState -> tcAccMinState - | FullState tcAccFullState -> tcAccFullState.tcAccMinState + | FullState(tcAccMinState, _) -> tcAccMinState /// Accumulated results of type checking. and [] TypeCheckAccumulator (tcConfig: TcConfig, @@ -1113,12 +1111,12 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member this.Next(input) = eventually { - let! state = this.GetState(true) - let lazyFullState = + let! prevState = this.GetState(true) + let lazyPrevFullState = eventually { - let! state = this.GetState(false) - match state with - | FullState fullState -> return Some fullState + let! prevState = this.GetState(false) + match prevState with + | FullState(_, prevFullState) -> return Some prevFullState | _ -> return None } return @@ -1132,19 +1130,19 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, state.Minimum, lazyFullState, input) + beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, input) } member this.Finish(finalTcErrorsRev, finalTopAttribs) = eventually { - let! state = this.GetState(true) + let! prevState = this.GetState(true) - let minState = { state.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs } - let lazyFullState = + let prevMinState = { prevState.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs } + let lazyPrevFullState = eventually { - let! state = this.GetState(false) - match state with - | FullState fullState -> return Some { fullState with tcAccMinState = minState } + let! prevState = this.GetState(false) + match prevState with + | FullState (_, prevFullState) -> return Some prevFullState | _ -> return None } @@ -1159,7 +1157,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, minState, lazyFullState, input) + beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, input) } member this.tcState = @@ -1183,21 +1181,21 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member _.tcResolutionsRev = eventually { match! this.GetState(false) with - | FullState state -> return state.tcResolutionsRev + | FullState(_, state) -> return state.tcResolutionsRev | _ -> return [] } member _.tcSymbolUsesRev = eventually { match! this.GetState(false) with - | FullState state -> return state.tcSymbolUsesRev + | FullState(_, state) -> return state.tcSymbolUsesRev | _ -> return [] } member _.tcOpenDeclarationsRev = eventually { match! this.GetState(false) with - | FullState state -> return state.tcOpenDeclarationsRev + | FullState(_, state) -> return state.tcOpenDeclarationsRev | _ -> return [] } @@ -1210,7 +1208,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member _.latestImplFile = eventually { match! this.GetState(false) with - | FullState state -> return state.latestImplFile + | FullState(_, state) -> return state.latestImplFile | _ -> return None } @@ -1238,14 +1236,14 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member _.itemKeyStore = eventually { match! this.GetState(false) with - | FullState state -> return state.itemKeyStore + | FullState(_, state) -> return state.itemKeyStore | _ -> return None } member _.semanticClassification = eventually { match! this.GetState(false) with - | FullState state -> return state.semanticClassification + | FullState(_, state) -> return state.semanticClassification | _ -> return [||] } @@ -1306,11 +1304,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, else match! prevTcFullAccState with | None -> return MinimumState minState - | Some fullState -> + | Some prevFullState -> /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - let tcResolutionsRev = fullState.tcResolutionsRev - let tcSymbolUsesRev = fullState.tcSymbolUsesRev - let tcOpenDeclarationsRev = fullState.tcOpenDeclarationsRev + let tcResolutionsRev = prevFullState.tcResolutionsRev + let tcSymbolUsesRev = prevFullState.tcSymbolUsesRev + let tcOpenDeclarationsRev = prevFullState.tcOpenDeclarationsRev // Build symbol keys let itemKeyStore, semanticClassification = @@ -1335,8 +1333,6 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, return { - tcAccMinState = minState - latestImplFile = if keepAssemblyContents then implFile else None tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev @@ -1675,7 +1671,6 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput } let tcAccFullState = { - tcAccMinState = tcAccMinState tcResolutionsRev=[] tcSymbolUsesRev=[] tcOpenDeclarationsRev=[] From 116b0b77d61e4adbcbc41d0176e9c9eb976eb480 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 09:55:25 -0700 Subject: [PATCH 14/34] working, but not quite optimized --- src/fsharp/service/IncrementalBuild.fs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 6a930e59af4..15974cf97dc 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1157,7 +1157,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, input) + beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, (None, range0, String.Empty, [||])) } member this.tcState = @@ -1331,7 +1331,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, else None, [||] - return + let fullState = { latestImplFile = if keepAssemblyContents then implFile else None tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev @@ -1340,7 +1340,8 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, itemKeyStore = itemKeyStore semanticClassification = semanticClassification } - |> FullState + + return FullState(minState, fullState) } @@ -1359,7 +1360,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, f ctok) return! timeSlicedComputation | _ -> - return MinimumState prevTcMinAccState + match! prevTcFullAccState with + | Some prevFullState -> + return FullState(prevTcMinAccState, prevFullState) + | _ -> + return MinimumState prevTcMinAccState } /// Global service state From 3fa36f6ada1f3765590c38d2f6467d9f86dbeec5 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 15:46:11 -0700 Subject: [PATCH 15/34] Fix dependency files --- src/fsharp/service/IncrementalBuild.fs | 16 ++++++++----- src/fsharp/service/IncrementalBuild.fsi | 2 +- src/fsharp/service/service.fs | 32 +++++++++++++------------ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 15974cf97dc..ad2fa9d13f5 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1038,6 +1038,8 @@ type TypeCheckAccumulatorMinimumState = /// Accumulated errors, last file first tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list + + tcDependencyFiles: string list } /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. @@ -1078,7 +1080,6 @@ type TypeCheckAccumulatorState = and [] TypeCheckAccumulator (tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, - tcDependencyFiles: string list, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, @@ -1124,7 +1125,6 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcConfig, tcGlobals, tcImports, - tcDependencyFiles, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, @@ -1151,7 +1151,6 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcConfig, tcGlobals, tcImports, - tcDependencyFiles, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, @@ -1219,7 +1218,10 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, } member _.tcDependencyFiles = - tcDependencyFiles + eventually { + let! state = this.GetState(true) + return state.Minimum.tcDependencyFiles + } member _.tcModuleNamesDict = eventually { @@ -1266,6 +1268,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, let tcModuleNamesDict = prevTcMinAccState.tcModuleNamesDict let tcState = prevTcMinAccState.tcState let tcErrorsRev = prevTcMinAccState.tcErrorsRev + let tcDependencyFiles = prevTcMinAccState.tcDependencyFiles ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) @@ -1297,6 +1300,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, latestCcuSigForFile = Some ccuSigForFile tcErrorsRev = newErrors :: tcErrorsRev topAttribs = Some topAttribs + tcDependencyFiles = filename :: tcDependencyFiles } if quickCheck then @@ -1456,7 +1460,7 @@ type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTi /// Disambiguation table for module names member _.ModuleNamesDict ctok = tcAcc.tcModuleNamesDict |> eval ctok - member _.TcDependencyFiles = tcAcc.tcDependencyFiles + member _.TcDependencyFiles ctok = tcAcc.tcDependencyFiles |> eval ctok member _.TopAttribs ctok = tcAcc.topAttribs |> eval ctok @@ -1673,6 +1677,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput latestCcuSigForFile=None tcErrorsRev = [ initialErrors ] tcModuleNamesDict = Map.empty + tcDependencyFiles = basicDependencies } let tcAccFullState = { @@ -1688,7 +1693,6 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tcConfig, tcGlobals, tcImports, - basicDependencies, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index c1a9dd098cb..17fe19d3992 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -73,7 +73,7 @@ type internal PartialCheckResults = /// Disambiguation table for module names member ModuleNamesDict: CompilationThreadToken -> ModuleNamesDict - member TcDependencyFiles: string list + member TcDependencyFiles: CompilationThreadToken -> string list /// Represents the collected attributes to apply to the module of assembly generates member TopAttribs: CompilationThreadToken -> TypeChecker.TopAttribs option diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 8b6d32097a8..e27513770e5 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -611,13 +611,13 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return tcPrior |> Option.map (fun tcPrior -> - (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) ) } match tcPrior with - | Some(tcPrior, tcState, tcErrors, moduleNamesDict) -> - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + | Some(tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles) -> + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) return Some checkResults | None -> return None // the incremental builder was not up to date finally @@ -642,13 +642,13 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) - let! tcPrior, tcState, tcErrors, moduleNamesDict = + let! tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles = execWithReactorAsync <| fun ctok -> cancellable { let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) - return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) } - let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) return checkAnswer finally bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) @@ -685,11 +685,11 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | _ -> // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, // but results with non cooperative blocking when a firts file from a project opened. - let! tcPrior, tcState, tcErrors, moduleNamesDict = + let! tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles = execWithReactorAsync <| fun ctok -> cancellable { let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) - return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok) + return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) } // Do the parsing. @@ -697,7 +697,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, filename, parsingOptions, userOpName, suggestNamesForErrors) let parseResults = FSharpParseFileResults(parseErrors, parseTreeOpt, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) Logger.LogBlockMessageStop (filename + strGuid + "-Successful") LogCompilerFunctionId.Service_ParseAndCheckFileInProject @@ -720,13 +720,14 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let latestCcuSigForFile = tcProj.LatestCcuSigForFile ctok - let tcState = tcProj.TcState ctok - let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok let tcResolutionsRev = tcProj.TcResolutionsRev ctok let tcSymbolUsesRev = tcProj.TcSymbolUsesRev ctok let tcOpenDeclarationsRev = tcProj.TcOpenDeclarationsRev ctok + let latestCcuSigForFile = tcProj.LatestCcuSigForFile ctok + let tcState = tcProj.TcState ctok + let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok let latestImplementationFile = tcProj.LatestImplementationFile ctok + let tcDependencyFiles = tcProj.TcDependencyFiles ctok let tcErrors = tcProj.TcErrors ctok @@ -743,7 +744,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC tcProj.TcGlobals, options.IsIncompleteTypeCheckEnvironment, builder, - Array.ofList tcProj.TcDependencyFiles, + Array.ofList tcDependencyFiles, creationErrors, parseResults.Errors, tcErrors, @@ -810,16 +811,17 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation + let tcSymbolUses = tcProj.TcSymbolUses ctok let topAttribs = tcProj.TopAttribs ctok let tcState = tcProj.TcState ctok let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok - let tcSymbolUses = tcProj.TcSymbolUses ctok let tcErrors = tcProj.TcErrors ctok + let tcDependencyFiles = tcProj.TcDependencyFiles ctok let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, tcSymbolUses, topAttribs, tcAssemblyDataOpt, ilAssemRef, - tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles)) + tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcDependencyFiles)) } /// Get the timestamp that would be on the output if fully built immediately From 79db9edf5d775b516458f433f8dab6d809d0d4d8 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 16:34:22 -0700 Subject: [PATCH 16/34] Refactor types --- src/fsharp/CompilerConfig.fs | 4 - src/fsharp/CompilerConfig.fsi | 2 - src/fsharp/service/IncrementalBuild.fs | 278 ++++++++---------------- src/fsharp/service/IncrementalBuild.fsi | 86 ++++---- src/fsharp/service/service.fs | 63 +++--- 5 files changed, 173 insertions(+), 260 deletions(-) diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index e83cb5d25c6..c3589d1bb15 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -246,8 +246,6 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit - abstract RunEventually: Eventually<'T> -> Async<'T option> - type ImportedAssembly = { ILScopeRef: ILScopeRef FSharpViewOfMetadata: CcuThunk @@ -609,8 +607,6 @@ type TcConfigBuilder = let ctok = CompilationThreadToken () { new ICompilationThread with member __.EnqueueWork work = work ctok - member __.RunEventually work = - Eventually.forceAsync (fun work -> async { return work ctok }) work } pause = false alwaysCallVirt = true diff --git a/src/fsharp/CompilerConfig.fsi b/src/fsharp/CompilerConfig.fsi index 584af0087dd..00e824b4054 100644 --- a/src/fsharp/CompilerConfig.fsi +++ b/src/fsharp/CompilerConfig.fsi @@ -100,8 +100,6 @@ type ICompilationThread = /// Enqueue work to be done on a compilation thread. abstract EnqueueWork: (CompilationThreadToken -> unit) -> unit - abstract RunEventually: Eventually<'T> -> Async<'T option> - [] type CompilerTarget = | WinExe diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index ad2fa9d13f5..b2780e74821 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1024,13 +1024,13 @@ module Tc = FSharp.Compiler.TypeChecker /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] -type TypeCheckAccumulatorMinimumState = +type TcInfo = { tcState: TcState tcEnvAtEndOfFile: TcEnv /// Disambiguation table for module names - tcModuleNamesDict: ModuleNamesDict + moduleNamesDict: ModuleNamesDict topAttribs: TopAttribs option @@ -1042,9 +1042,12 @@ type TypeCheckAccumulatorMinimumState = tcDependencyFiles: string list } -/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. + member x.TcErrors = + Array.concat (List.rev x.tcErrorsRev) + +/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for tooling. [] -type TypeCheckAccumulatorFullState = +type TcInfoOptional = { /// Accumulated resolutions, last file first tcResolutionsRev: TcResolutions list @@ -1065,16 +1068,19 @@ type TypeCheckAccumulatorFullState = semanticClassification: struct (range * SemanticClassificationType) [] } + member x.TcSymbolUses = + List.rev x.tcSymbolUsesRev + /// Accumulated results of type checking. [] -type TypeCheckAccumulatorState = - | MinimumState of TypeCheckAccumulatorMinimumState - | FullState of TypeCheckAccumulatorMinimumState * TypeCheckAccumulatorFullState +type TcInfoState = + | MinimumState of TcInfo + | FullState of TcInfo * TcInfoOptional member this.Minimum = match this with - | MinimumState tcAccMinState -> tcAccMinState - | FullState(tcAccMinState, _) -> tcAccMinState + | MinimumState tcInfo -> tcInfo + | FullState(tcInfo, _) -> tcInfo /// Accumulated results of type checking. and [] TypeCheckAccumulator (tcConfig: TcConfig, @@ -1085,29 +1091,35 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, enableBackgroundItemKeyStoreAndSemanticClassification, beforeFileChecked: Event, fileChecked: Event, - prevTcMinAccState: TypeCheckAccumulatorMinimumState, - prevTcFullAccState: Eventually, - input) as this = + prevTcInfo: TcInfo, + prevTcInfoOptional: Eventually, + input) = + + let lazyTcInfoState: TcInfoState option ref = ref None - let lazyTcAccState: TypeCheckAccumulatorState option ref = ref None + member _.TcConfig = tcConfig + + member _.TcGlobals = tcGlobals + + member _.TcImports = tcImports member this.GetState(quickCheck: bool) = let mustCheck = - match !lazyTcAccState, quickCheck with + match !lazyTcInfoState, quickCheck with | None, _ -> true | Some(MinimumState _), false -> true | _ -> false if mustCheck then - lazyTcAccState := None + lazyTcInfoState := None - match !lazyTcAccState with - | Some tcAccState -> tcAccState |> Eventually.Done + match !lazyTcInfoState with + | Some tcInfoState -> tcInfoState |> Eventually.Done | _ -> eventually { - let! tcAccState = this.TypeCheck(quickCheck) - lazyTcAccState := Some tcAccState - return tcAccState + let! tcInfoState = this.TypeCheck(quickCheck) + lazyTcInfoState := Some tcInfoState + return tcInfoState } member this.Next(input) = @@ -1159,98 +1171,32 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, (None, range0, String.Empty, [||])) } - member this.tcState = - eventually { - let! state = this.GetState(true) - return state.Minimum.tcState - } - - member _.tcImports = tcImports - - member _.tcGlobals = tcGlobals - - member _.tcConfig = tcConfig - - member _.tcEnvAtEndOfFile = - eventually { - let! state = this.GetState(true) - return state.Minimum.tcEnvAtEndOfFile - } - - member _.tcResolutionsRev = - eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.tcResolutionsRev - | _ -> return [] - } - - member _.tcSymbolUsesRev = - eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.tcSymbolUsesRev - | _ -> return [] - } - - member _.tcOpenDeclarationsRev = - eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.tcOpenDeclarationsRev - | _ -> return [] - } - - member _.topAttribs = - eventually { - let! state = this.GetState(true) - return state.Minimum.topAttribs - } - - member _.latestImplFile = - eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.latestImplFile - | _ -> return None - } - - member _.latestCcuSigForFile = - eventually { - let! state = this.GetState(true) - return state.Minimum.latestCcuSigForFile - } - - member _.tcDependencyFiles = - eventually { - let! state = this.GetState(true) - return state.Minimum.tcDependencyFiles - } - - member _.tcModuleNamesDict = - eventually { - let! state = this.GetState(true) - return state.Minimum.tcModuleNamesDict - } - - member _.tcErrorsRev = + member this.TcInfo = eventually { let! state = this.GetState(true) - return state.Minimum.tcErrorsRev + return state.Minimum } - member _.itemKeyStore = + member this.TcInfoFull = eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.itemKeyStore - | _ -> return None - } - - member _.semanticClassification = - eventually { - match! this.GetState(false) with - | FullState(_, state) -> return state.semanticClassification - | _ -> return [||] + let! state = this.GetState(false) + match state with + | FullState(tcInfo, tcInfoOptional) -> return tcInfo, tcInfoOptional + | MinimumState tcInfo -> + return + tcInfo, + { + tcResolutionsRev = [] + tcSymbolUsesRev = [] + tcOpenDeclarationsRev = [] + latestImplFile = None + itemKeyStore = None + semanticClassification = [||] + } } member _.TypeCheck (quickCheck: bool) = - match quickCheck, !lazyTcAccState with + match quickCheck, !lazyTcInfoState with | true, Some (MinimumState _ as state) | true, Some (FullState _ as state) -> state |> Eventually.Done | false, Some (FullState _ as state) -> state |> Eventually.Done @@ -1265,15 +1211,15 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, let fullComputation = eventually { beforeFileChecked.Trigger filename - let tcModuleNamesDict = prevTcMinAccState.tcModuleNamesDict - let tcState = prevTcMinAccState.tcState - let tcErrorsRev = prevTcMinAccState.tcErrorsRev - let tcDependencyFiles = prevTcMinAccState.tcDependencyFiles + let prevModuleNamesDict = prevTcInfo.moduleNamesDict + let prevTcState = prevTcInfo.tcState + let prevTcErrorsRev = prevTcInfo.tcErrorsRev + let prevTcDependencyFiles = prevTcInfo.tcDependencyFiles ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) - let input, moduleNamesDict = DeduplicateParsedInputModuleName tcModuleNamesDict input + let input, moduleNamesDict = DeduplicateParsedInputModuleName prevModuleNamesDict input Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = @@ -1283,7 +1229,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcGlobals, None, TcResultsSink.WithSink sink, - tcState, input, + prevTcState, input, quickCheck) Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck @@ -1292,27 +1238,27 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls - let minState = + let tcInfo = { tcState = tcState tcEnvAtEndOfFile = tcEnvAtEndOfFile - tcModuleNamesDict = moduleNamesDict + moduleNamesDict = moduleNamesDict latestCcuSigForFile = Some ccuSigForFile - tcErrorsRev = newErrors :: tcErrorsRev + tcErrorsRev = newErrors :: prevTcErrorsRev topAttribs = Some topAttribs - tcDependencyFiles = filename :: tcDependencyFiles + tcDependencyFiles = filename :: prevTcDependencyFiles } if quickCheck then - return MinimumState minState + return MinimumState tcInfo else - match! prevTcFullAccState with - | None -> return MinimumState minState - | Some prevFullState -> + match! prevTcInfoOptional with + | None -> return MinimumState tcInfo + | Some prevTcInfoOptional -> /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - let tcResolutionsRev = prevFullState.tcResolutionsRev - let tcSymbolUsesRev = prevFullState.tcSymbolUsesRev - let tcOpenDeclarationsRev = prevFullState.tcOpenDeclarationsRev + let tcResolutionsRev = prevTcInfoOptional.tcResolutionsRev + let tcSymbolUsesRev = prevTcInfoOptional.tcSymbolUsesRev + let tcOpenDeclarationsRev = prevTcInfoOptional.tcOpenDeclarationsRev // Build symbol keys let itemKeyStore, semanticClassification = @@ -1335,7 +1281,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, else None, [||] - let fullState = + let tcInfoOptional = { latestImplFile = if keepAssemblyContents then implFile else None tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev @@ -1345,7 +1291,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, semanticClassification = semanticClassification } - return FullState(minState, fullState) + return FullState(tcInfo, tcInfoOptional) } @@ -1364,11 +1310,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, f ctok) return! timeSlicedComputation | _ -> - match! prevTcFullAccState with - | Some prevFullState -> - return FullState(prevTcMinAccState, prevFullState) + match! prevTcInfoOptional with + | Some prevTcInfoOptional -> + return FullState(prevTcInfo, prevTcInfoOptional) | _ -> - return MinimumState prevTcMinAccState + return MinimumState prevTcInfo } /// Global service state @@ -1432,58 +1378,25 @@ type FrameworkImportsCache(keepStrongly) = /// Represents the interim state of checking an assembly [] -type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime, _thread: ICompilationThread) = +type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime) = let eval ctok (work: Eventually<'T>) = match work with | Eventually.Done res -> res | _ -> Eventually.force ctok work - member _.TcState ctok = tcAcc.tcState |> eval ctok - member _.TcImports = tcAcc.tcImports - member _.TcGlobals = tcAcc.tcGlobals - member _.TcConfig = tcAcc.tcConfig - member _.TcEnvAtEnd ctok = tcAcc.tcEnvAtEndOfFile |> eval ctok - - /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcErrorsRev ctok = tcAcc.tcErrorsRev |> eval ctok - - /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcResolutionsRev ctok = tcAcc.tcResolutionsRev |> eval ctok - - /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcSymbolUsesRev ctok = tcAcc.tcSymbolUsesRev |> eval ctok - - /// Kept in a stack so that each incremental update shares storage with previous files - member _.TcOpenDeclarationsRev ctok = tcAcc.tcOpenDeclarationsRev |> eval ctok - - /// Disambiguation table for module names - member _.ModuleNamesDict ctok = tcAcc.tcModuleNamesDict |> eval ctok - - member _.TcDependencyFiles ctok = tcAcc.tcDependencyFiles |> eval ctok - - member _.TopAttribs ctok = tcAcc.topAttribs |> eval ctok + member _.TcImports = tcAcc.TcImports + member _.TcGlobals = tcAcc.TcGlobals + member _.TcConfig = tcAcc.TcConfig member _.TimeStamp = timeStamp - - member _.LatestImplementationFile ctok = tcAcc.latestImplFile |> eval ctok - - member _.LatestCcuSigForFile ctok = tcAcc.latestCcuSigForFile |> eval ctok - - member _.ItemKeyStore ctok = tcAcc.itemKeyStore |> eval ctok - - member _.SemanticClassification ctok = tcAcc.semanticClassification |> eval ctok - member x.TcErrors ctok = - let tcErrorsRev = x.TcErrorsRev ctok - Array.concat (List.rev tcErrorsRev) + member _.TcInfo ctok = tcAcc.TcInfo |> eval ctok - member x.TcSymbolUses ctok = - let tcSymbolUsesRev = x.TcSymbolUsesRev ctok - List.rev tcSymbolUsesRev + member _.TcInfoFull ctok = tcAcc.TcInfoFull |> eval ctok - static member Create (tcAcc: TypeCheckAccumulator, timestamp, thread) = - PartialCheckResults(tcAcc, timestamp, thread) + static member Create (tcAcc: TypeCheckAccumulator, timestamp) = + PartialCheckResults(tcAcc, timestamp) [] module Utilities = @@ -1676,7 +1589,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput topAttribs=None latestCcuSigForFile=None tcErrorsRev = [ initialErrors ] - tcModuleNamesDict = Map.empty + moduleNamesDict = Map.empty tcDependencyFiles = basicDependencies } let tcAccFullState = @@ -1722,8 +1635,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput // Get the state at the end of the type-checking of the last file let finalAcc = tcStates.[tcStates.Length-1] - let finalAccTcState = finalAcc.tcState |> Eventually.force ctok - let finalAccTcErrorsRev = finalAcc.tcErrorsRev |> Eventually.force ctok + let finalInfo = finalAcc.TcInfo |> Eventually.force ctok // Finish the checking let (_tcEnvAtEndOfLastFile, topAttrs, mimpls, _), tcState = @@ -1731,12 +1643,9 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tcStates |> List.ofArray |> List.map (fun acc -> - let tcEnvAtEndOfFile = acc.tcEnvAtEndOfFile |> Eventually.force ctok - let topAttribs = acc.topAttribs |> Eventually.force ctok - let latestImplFile = acc.latestImplFile |> Eventually.force ctok - let latestCcuSigForFile = acc.latestCcuSigForFile |> Eventually.force ctok - tcEnvAtEndOfFile, defaultArg topAttribs EmptyTopAttrs, latestImplFile, latestCcuSigForFile) - TypeCheckMultipleInputsFinish (results, finalAccTcState) + let tcInfo, tcInfoOptional = acc.TcInfoFull |> Eventually.force ctok + tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, tcInfoOptional.latestImplFile, tcInfo.latestCcuSigForFile) + TypeCheckMultipleInputsFinish (results, finalInfo.tcState) let ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = try @@ -1793,7 +1702,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput errorRecoveryNoRange e mkSimpleAssemblyRef assemblyName, None, None - let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalAccTcErrorsRev), Some topAttrs) |> Eventually.force ctok + let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalInfo.tcErrorsRev), Some topAttrs) |> Eventually.force ctok return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalAccWithErrors } @@ -1879,7 +1788,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput | _ -> GetVectorResultBySlot(tcStatesNode, slotOfFile-1, partialBuild) match result with - | Some (tcAcc, timestamp) -> Some (PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread)) + | Some (tcAcc, timestamp) -> Some (PartialCheckResults.Create (tcAcc, timestamp)) | _ -> None @@ -1905,7 +1814,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput } match result with - | Some (tcAcc, timestamp) -> return PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread) + | Some (tcAcc, timestamp) -> return PartialCheckResults.Create (tcAcc, timestamp) | None -> return! failwith "Build was not evaluated, expected the results to be ready after 'Eval' (GetCheckResultsBeforeSlotInProject)." } @@ -1926,7 +1835,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let! build = IncrementalBuild.Eval cache ctok SavePartialBuild finalizedTypeCheckNode partialBuild match GetScalarResult(finalizedTypeCheckNode, build) with | Some ((ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, tcAcc), timestamp) -> - return PartialCheckResults.Create (tcAcc, timestamp, tcConfig.compilationThread), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt + return PartialCheckResults.Create (tcAcc, timestamp), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt | None -> // helpers to diagnose https://github.com/Microsoft/visualfsharp/pull/2460/ let brname = match GetTopLevelExprByName(build, finalizedTypeCheckNode.Name) with ScalarBuildRule se ->se.Id | _ -> Id 0xdeadbeef @@ -2063,13 +1972,6 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput Reactor.Singleton.EnqueueOp ("Unknown", "ICompilationThread.EnqueueWork", "work", fun ctok -> work ctok ) - member __.RunEventually work = - work - |> Eventually.forceAsync - (fun work -> - Reactor.Singleton.EnqueueAndAwaitOpAsync("Unknown", "ICompilationThread.RunEventually", "work", - fun ctok -> cancellable.Return(work ctok) - )) } tcConfigB, sourceFilesNew diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 17fe19d3992..a749b573ae2 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -42,60 +42,70 @@ module internal IncrementalBuilderEventTesting = val GetMostRecentIncrementalBuildEvents : int -> IBEvent list val GetCurrentIncrementalBuildEventNum : unit -> int -/// Represents the state in the incremental graph associated with checking a file -[] -type internal PartialCheckResults = - - /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcState : CompilationThreadToken -> TcState - - member TcImports: TcImports - - member TcGlobals: TcGlobals +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcInfo = + { + tcState: TcState + tcEnvAtEndOfFile: TypeChecker.TcEnv - member TcConfig: TcConfig + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict - /// This field is None if a major unrecovered error occurred when preparing the initial state - member TcEnvAtEnd : CompilationThreadToken -> TypeChecker.TcEnv + topAttribs: TypeChecker.TopAttribs option - /// Represents the collected errors from type checking - member TcErrorsRev : CompilationThreadToken -> (PhasedDiagnostic * FSharpErrorSeverity)[] list + latestCcuSigForFile: ModuleOrNamespaceType option - /// Represents the collected name resolutions from type checking - member TcResolutionsRev: CompilationThreadToken -> TcResolutions list + /// Accumulated errors, last file first + tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list - /// Represents the collected uses of symbols from type checking - member TcSymbolUsesRev: CompilationThreadToken -> TcSymbolUses list + tcDependencyFiles: string list + } - /// Represents open declarations - member TcOpenDeclarationsRev: CompilationThreadToken -> OpenDeclaration[] list + member TcErrors: (PhasedDiagnostic * FSharpErrorSeverity)[] - /// Disambiguation table for module names - member ModuleNamesDict: CompilationThreadToken -> ModuleNamesDict +/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for tooling. +[] +type internal TcInfoOptional = + { + /// Accumulated resolutions, last file first + tcResolutionsRev: TcResolutions list - member TcDependencyFiles: CompilationThreadToken -> string list + /// Accumulated symbol uses, last file first + tcSymbolUsesRev: TcSymbolUses list - /// Represents the collected attributes to apply to the module of assembly generates - member TopAttribs: CompilationThreadToken -> TypeChecker.TopAttribs option + /// Accumulated 'open' declarations, last file first + tcOpenDeclarationsRev: OpenDeclaration[] list - member TimeStamp: DateTime - - /// Represents latest complete typechecked implementation file, including its typechecked signature if any. - /// Empty for a signature file. - member LatestImplementationFile: CompilationThreadToken -> TypedImplFile option - - /// Represents latest inferred signature contents. - member LatestCcuSigForFile: CompilationThreadToken -> ModuleOrNamespaceType option + /// Result of checking most recent file, if any + latestImplFile: TypedImplFile option /// If enabled, stores a linear list of ranges and strings that identify an Item(symbol) in a file. Used for background find all references. - member ItemKeyStore: CompilationThreadToken -> ItemKeyStore option + itemKeyStore: ItemKeyStore option /// If enabled, holds semantic classification information for Item(symbol)s in a file. - member SemanticClassification: CompilationThreadToken -> struct (range * SemanticClassificationType) [] + semanticClassification: struct (range * SemanticClassificationType) [] + } - member TcErrors: CompilationThreadToken -> (PhasedDiagnostic * FSharpErrorSeverity)[] + member TcSymbolUses: TcSymbolUses list - member TcSymbolUses: CompilationThreadToken -> TcSymbolUses list +/// Represents the state in the incremental graph associated with checking a file +[] +type internal PartialCheckResults = + + member TcImports: TcImports + + member TcGlobals: TcGlobals + + member TcConfig: TcConfig + + member TimeStamp: DateTime + + member TcInfo: CompilationThreadToken -> TcInfo + + member TcInfoFull: CompilationThreadToken -> TcInfo * TcInfoOptional + + member TimeStamp: DateTime /// Manages an incremental build graph for the build of an F# project [] diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index e27513770e5..e75bcddb007 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -611,13 +611,13 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return tcPrior |> Option.map (fun tcPrior -> - (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) + (tcPrior, tcPrior.TcInfo ctok) ) } match tcPrior with - | Some(tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles) -> - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + | Some(tcPrior, tcInfo) -> + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcInfo.tcDependencyFiles, tcPrior.TimeStamp, tcInfo.tcState, tcInfo.moduleNamesDict, tcInfo.TcErrors, creationErrors, userOpName) return Some checkResults | None -> return None // the incremental builder was not up to date finally @@ -642,13 +642,13 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) - let! tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles = + let! tcPrior, tcInfo = execWithReactorAsync <| fun ctok -> cancellable { let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) - return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) + return (tcPrior, tcPrior.TcInfo ctok) } - let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcInfo.tcDependencyFiles, tcPrior.TimeStamp, tcInfo.tcState, tcInfo.moduleNamesDict, tcInfo.TcErrors, creationErrors, userOpName) return checkAnswer finally bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) @@ -685,11 +685,11 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | _ -> // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, // but results with non cooperative blocking when a firts file from a project opened. - let! tcPrior, tcState, tcErrors, moduleNamesDict, tcDependencyFiles = + let! tcPrior, tcInfo = execWithReactorAsync <| fun ctok -> cancellable { let! tcPrior = builder.GetCheckResultsBeforeFileInProject (ctok, filename) - return (tcPrior, tcPrior.TcState ctok, tcPrior.TcErrors ctok, tcPrior.ModuleNamesDict ctok, tcPrior.TcDependencyFiles ctok) + return (tcPrior, tcPrior.TcInfo ctok) } // Do the parsing. @@ -697,7 +697,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, filename, parsingOptions, userOpName, suggestNamesForErrors) let parseResults = FSharpParseFileResults(parseErrors, parseTreeOpt, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcDependencyFiles, tcPrior.TimeStamp, tcState, moduleNamesDict, tcErrors, creationErrors, userOpName) + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcInfo.tcDependencyFiles, tcPrior.TimeStamp, tcInfo.tcState, tcInfo.moduleNamesDict, tcInfo.TcErrors, creationErrors, userOpName) Logger.LogBlockMessageStop (filename + strGuid + "-Successful") LogCompilerFunctionId.Service_ParseAndCheckFileInProject @@ -719,17 +719,19 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some builder -> let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - - let tcResolutionsRev = tcProj.TcResolutionsRev ctok - let tcSymbolUsesRev = tcProj.TcSymbolUsesRev ctok - let tcOpenDeclarationsRev = tcProj.TcOpenDeclarationsRev ctok - let latestCcuSigForFile = tcProj.LatestCcuSigForFile ctok - let tcState = tcProj.TcState ctok - let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok - let latestImplementationFile = tcProj.LatestImplementationFile ctok - let tcDependencyFiles = tcProj.TcDependencyFiles ctok - - let tcErrors = tcProj.TcErrors ctok + + let tcInfo, tcInfoOptional = tcProj.TcInfoFull ctok + + let tcResolutionsRev = tcInfoOptional.tcResolutionsRev + let tcSymbolUsesRev = tcInfoOptional.tcSymbolUsesRev + let tcOpenDeclarationsRev = tcInfoOptional.tcOpenDeclarationsRev + let latestCcuSigForFile = tcInfo.latestCcuSigForFile + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let latestImplementationFile = tcInfoOptional.latestImplFile + let tcDependencyFiles = tcInfo.tcDependencyFiles + + let tcErrors = tcInfo.TcErrors let errorOptions = builder.TcConfig.errorSeverityOptions let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] @@ -773,7 +775,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some builder -> if builder.ContainsFile filename then let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - match checkResults.ItemKeyStore ctok with + let _, tcInfoOptional = checkResults.TcInfoFull ctok + match tcInfoOptional.itemKeyStore with | None -> return Seq.empty | Some reader -> return reader.FindAll symbol.Item else @@ -788,7 +791,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> return [||] | Some builder -> let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - return checkResults.SemanticClassification ctok }) + let _, tcInfoOptional = checkResults.TcInfoFull ctok + return tcInfoOptional.semanticClassification }) /// Try to get recent approximate type check results for a file. member __.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = @@ -811,12 +815,15 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation - let tcSymbolUses = tcProj.TcSymbolUses ctok - let topAttribs = tcProj.TopAttribs ctok - let tcState = tcProj.TcState ctok - let tcEnvAtEnd = tcProj.TcEnvAtEnd ctok - let tcErrors = tcProj.TcErrors ctok - let tcDependencyFiles = tcProj.TcDependencyFiles ctok + + let tcInfo, tcInfoOptional = tcProj.TcInfoFull ctok + + let tcSymbolUses = tcInfoOptional.TcSymbolUses + let topAttribs = tcInfo.topAttribs + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let tcErrors = tcInfo.TcErrors + let tcDependencyFiles = tcInfo.tcDependencyFiles let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] return FSharpCheckProjectResults (options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, From 8350e370fa5b308a5efb00fcf514b4039cc2dbea Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 16:57:34 -0700 Subject: [PATCH 17/34] Added 'enableLazyTypeChecking'. --- src/fsharp/service/IncrementalBuild.fs | 12 ++++++++++++ src/fsharp/service/IncrementalBuild.fsi | 1 + src/fsharp/service/service.fs | 13 ++++++++----- src/fsharp/service/service.fsi | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index b2780e74821..4258232d2a2 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1089,6 +1089,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, beforeFileChecked: Event, fileChecked: Event, prevTcInfo: TcInfo, @@ -1104,6 +1105,11 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, member _.TcImports = tcImports member this.GetState(quickCheck: bool) = + let quickCheck = + // Only enable quick checks if we have enabled lazy type checking. + if enableLazyTypeChecking then quickCheck + else false + let mustCheck = match !lazyTcInfoState, quickCheck with | None, _ -> true @@ -1142,6 +1148,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, input) } @@ -1168,6 +1175,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, (None, range0, String.Empty, [||])) } @@ -1446,6 +1454,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, dependencyProviderOpt: DependencyProvider option) = let tcConfigP = TcConfigProvider.Constant tcConfig @@ -1611,6 +1620,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, beforeFileChecked, fileChecked, tcAccMinState, Eventually.Done (Some tcAccFullState), (None, range0, String.Empty, [||])) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft @@ -1912,6 +1922,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking: bool, dependencyProviderOpt) = let useSimpleResolutionSwitch = "--simpleresolution" @@ -2037,6 +2048,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, dependencyProviderOpt) return Some builder with e -> diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index a749b573ae2..4180f53ae59 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -214,6 +214,7 @@ type internal IncrementalBuilder = suggestNamesForErrors: bool * keepAllBackgroundSymbolUses: bool * enableBackgroundItemKeyStoreAndSemanticClassification: bool * + enableLazyTypeChecking: bool * dependencyProvider: DependencyProvider option -> Cancellable diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index e75bcddb007..d45769f7720 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -255,7 +255,7 @@ type ScriptClosureCacheToken() = interface LockToken // There is only one instance of this type, held in FSharpChecker -type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) as self = +type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) as self = // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.reactor: The one and only Reactor let reactor = Reactor.Singleton let beforeFileChecked = Event() @@ -323,6 +323,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC options.UseScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, FSharpCheckerResultsSettings.maxTimeShareMilliseconds, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, (if options.UseScriptResolutionRules then Some dependencyProviderForScripts else None)) match builderOpt with @@ -1021,9 +1022,10 @@ type FSharpChecker(legacyReferenceResolver, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification) = + enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking) = - let backgroundCompiler = BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) + let backgroundCompiler = BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) static let globalInstance = lazy FSharpChecker.Create() @@ -1040,7 +1042,7 @@ type FSharpChecker(legacyReferenceResolver, let maxMemEvent = new Event() /// Instantiate an interactive checker. - static member Create(?projectCacheSize, ?keepAssemblyContents, ?keepAllBackgroundResolutions, ?legacyReferenceResolver, ?tryGetMetadataSnapshot, ?suggestNamesForErrors, ?keepAllBackgroundSymbolUses, ?enableBackgroundItemKeyStoreAndSemanticClassification) = + static member Create(?projectCacheSize, ?keepAssemblyContents, ?keepAllBackgroundResolutions, ?legacyReferenceResolver, ?tryGetMetadataSnapshot, ?suggestNamesForErrors, ?keepAllBackgroundSymbolUses, ?enableBackgroundItemKeyStoreAndSemanticClassification, ?enableLazyTypeChecking) = let legacyReferenceResolver = match legacyReferenceResolver with @@ -1054,7 +1056,8 @@ type FSharpChecker(legacyReferenceResolver, let suggestNamesForErrors = defaultArg suggestNamesForErrors false let keepAllBackgroundSymbolUses = defaultArg keepAllBackgroundSymbolUses true let enableBackgroundItemKeyStoreAndSemanticClassification = defaultArg enableBackgroundItemKeyStoreAndSemanticClassification false - new FSharpChecker(legacyReferenceResolver, projectCacheSizeReal,keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification) + let enableLazyTypeChecking = defaultArg enableLazyTypeChecking false + new FSharpChecker(legacyReferenceResolver, projectCacheSizeReal,keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) member __.ReferenceResolver = legacyReferenceResolver diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index c0821183902..05cbea9548a 100755 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -81,7 +81,7 @@ type public FSharpChecker = static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: ReferenceResolver.Resolver * ?tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * - ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool + ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enableLazyTypeChecking: bool -> FSharpChecker /// From 75f8099621be1bae392c4e34db41adef67c93a9b Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 17:20:43 -0700 Subject: [PATCH 18/34] Enable lazy type checking in VS --- src/fsharp/service/IncrementalBuild.fs | 2 +- .../src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 4258232d2a2..f1eebabec17 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1236,7 +1236,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, tcConfig, tcImports, tcGlobals, None, - TcResultsSink.WithSink sink, + (if quickCheck then TcResultsSink.NoSink else TcResultsSink.WithSink sink), prevTcState, input, quickCheck) Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 176e0f8ccbd..9f32e41b974 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -60,7 +60,8 @@ type internal FSharpCheckerProvider legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), tryGetMetadataSnapshot = tryGetMetadataSnapshot, keepAllBackgroundSymbolUses = false, - enableBackgroundItemKeyStoreAndSemanticClassification = true) + enableBackgroundItemKeyStoreAndSemanticClassification = true, + enableLazyTypeChecking = true) // This is one half of the bridge between the F# background builder and the Roslyn analysis engine. // When the F# background builder refreshes the background semantic build context for a file, From 7be6e072c5722583f5517604940e65233a9c76fc Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 17:24:08 -0700 Subject: [PATCH 19/34] Added documentation --- src/fsharp/service/service.fsi | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index 05cbea9548a..a8b7d0af42a 100755 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -78,6 +78,7 @@ type public FSharpChecker = /// Indicate whether name suggestion should be enabled /// Indicate whether all symbol uses should be kept in background checking /// Indicates whether a table of symbol keys should be kept for background compilation + /// Indicates whether to perform partial type checking. Performance will vary depending on how existing APIs are used. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: ReferenceResolver.Resolver * ?tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * From 57611bcee5a842743b9d768c7737cdec1c32a260 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 18:40:39 -0700 Subject: [PATCH 20/34] Added SyntaxTreeAccumulator --- src/fsharp/service/IncrementalBuild.fs | 316 ++++++++++-------- .../SurfaceArea.netstandard.fs | 2 +- 2 files changed, 172 insertions(+), 146 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index f1eebabec17..8be1817e488 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1082,22 +1082,64 @@ type TcInfoState = | MinimumState tcInfo -> tcInfo | FullState(tcInfo, _) -> tcInfo +[] +type SyntaxTreeAccumulator (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, filename: string, isLastCompiland) = + + let mutable weakCache: WeakReference<_> option = None + + let parse () = + let errorLogger = CompilationErrorLogger("Parse", tcConfig.errorSeverityOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) + + try + IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed filename) + let input = ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) + fileParsed.Trigger filename + + let res = input, sourceRange, filename, errorLogger.GetErrors () + weakCache <- Some(WeakReference<_>(res)) + res + with exn -> + let msg = sprintf "unexpected failure in SyntaxTree.parse\nerror = %s" (exn.ToString()) + System.Diagnostics.Debug.Assert(false, msg) + failwith msg + + /// Parse the given file and return the given input. + member _.Parse () = + match weakCache with + | Some weakCache -> + match weakCache.TryGetTarget() with + | true, res -> res + | _ -> parse () + | _ -> parse () + /// Accumulated results of type checking. -and [] TypeCheckAccumulator (tcConfig: TcConfig, - tcGlobals: TcGlobals, - tcImports: TcImports, - keepAssemblyContents, keepAllBackgroundResolutions, - maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, - beforeFileChecked: Event, - fileChecked: Event, - prevTcInfo: TcInfo, - prevTcInfoOptional: Eventually, - input) = +[] +type TypeCheckAccumulator ( tcConfig: TcConfig, + tcGlobals: TcGlobals, + tcImports: TcImports, + keepAssemblyContents, keepAllBackgroundResolutions, + maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enableLazyTypeChecking, + beforeFileChecked: Event, + fileChecked: Event, + prevTcInfo: TcInfo, + prevTcInfoOptional: Eventually, + syntaxTreeOpt: SyntaxTreeAccumulator option) = let lazyTcInfoState: TcInfoState option ref = ref None + let defaultTypeCheck () = + eventually { + match! prevTcInfoOptional with + | Some prevTcInfoOptional -> + return FullState(prevTcInfo, prevTcInfoOptional) + | _ -> + return MinimumState prevTcInfo + } + member _.TcConfig = tcConfig member _.TcGlobals = tcGlobals @@ -1128,7 +1170,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, return tcInfoState } - member this.Next(input) = + member this.Next(syntaxTree) = eventually { let! prevState = this.GetState(true) let lazyPrevFullState = @@ -1149,7 +1191,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking, - beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, input) + beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, Some syntaxTree) } member this.Finish(finalTcErrorsRev, finalTopAttribs) = @@ -1176,7 +1218,7 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking, - beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, (None, range0, String.Empty, [||])) + beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, None) } member this.TcInfo = @@ -1211,118 +1253,117 @@ and [] TypeCheckAccumulator (tcConfig: TcConfig, | _ -> eventually { - match input with - | Some input, _sourceRange, filename, parseErrors -> - IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) - let capturingErrorLogger = CompilationErrorLogger("TypeCheck", tcConfig.errorSeverityOptions) - let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput input, capturingErrorLogger) - let fullComputation = - eventually { - beforeFileChecked.Trigger filename - let prevModuleNamesDict = prevTcInfo.moduleNamesDict - let prevTcState = prevTcInfo.tcState - let prevTcErrorsRev = prevTcInfo.tcErrorsRev - let prevTcDependencyFiles = prevTcInfo.tcDependencyFiles - - ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore - let sink = TcResultsSinkImpl(tcGlobals) - let hadParseErrors = not (Array.isEmpty parseErrors) - let input, moduleNamesDict = DeduplicateParsedInputModuleName prevModuleNamesDict input - - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck - let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = - TypeCheckOneInputEventually - ((fun () -> hadParseErrors || errorLogger.ErrorCount > 0), - tcConfig, tcImports, - tcGlobals, - None, - (if quickCheck then TcResultsSink.NoSink else TcResultsSink.WithSink sink), - prevTcState, input, - quickCheck) - Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck - - fileChecked.Trigger filename - let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) - - let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls - - let tcInfo = - { - tcState = tcState - tcEnvAtEndOfFile = tcEnvAtEndOfFile - moduleNamesDict = moduleNamesDict - latestCcuSigForFile = Some ccuSigForFile - tcErrorsRev = newErrors :: prevTcErrorsRev - topAttribs = Some topAttribs - tcDependencyFiles = filename :: prevTcDependencyFiles - } - - if quickCheck then - return MinimumState tcInfo - else - match! prevTcInfoOptional with - | None -> return MinimumState tcInfo - | Some prevTcInfoOptional -> - /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - let tcResolutionsRev = prevTcInfoOptional.tcResolutionsRev - let tcSymbolUsesRev = prevTcInfoOptional.tcSymbolUsesRev - let tcOpenDeclarationsRev = prevTcInfoOptional.tcOpenDeclarationsRev + match syntaxTreeOpt with + | None -> return! defaultTypeCheck () + | Some syntaxTree -> + match syntaxTree.Parse () with + | Some input, _sourceRange, filename, parseErrors -> + IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) + let capturingErrorLogger = CompilationErrorLogger("TypeCheck", tcConfig.errorSeverityOptions) + let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput input, capturingErrorLogger) + let fullComputation = + eventually { + beforeFileChecked.Trigger filename + let prevModuleNamesDict = prevTcInfo.moduleNamesDict + let prevTcState = prevTcInfo.tcState + let prevTcErrorsRev = prevTcInfo.tcErrorsRev + let prevTcDependencyFiles = prevTcInfo.tcDependencyFiles + + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName filename, tcImports.DependencyProvider) |> ignore + let sink = TcResultsSinkImpl(tcGlobals) + let hadParseErrors = not (Array.isEmpty parseErrors) + let input, moduleNamesDict = DeduplicateParsedInputModuleName prevModuleNamesDict input + + Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_TypeCheck + let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = + TypeCheckOneInputEventually + ((fun () -> hadParseErrors || errorLogger.ErrorCount > 0), + tcConfig, tcImports, + tcGlobals, + None, + (if quickCheck then TcResultsSink.NoSink else TcResultsSink.WithSink sink), + prevTcState, input, + quickCheck) + Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck + + fileChecked.Trigger filename + let newErrors = Array.append parseErrors (capturingErrorLogger.GetErrors()) + + let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls + + let tcInfo = + { + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + moduleNamesDict = moduleNamesDict + latestCcuSigForFile = Some ccuSigForFile + tcErrorsRev = newErrors :: prevTcErrorsRev + topAttribs = Some topAttribs + tcDependencyFiles = filename :: prevTcDependencyFiles + } + + if quickCheck then + return MinimumState tcInfo + else + match! prevTcInfoOptional with + | None -> return MinimumState tcInfo + | Some prevTcInfoOptional -> + /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away + let tcResolutionsRev = prevTcInfoOptional.tcResolutionsRev + let tcSymbolUsesRev = prevTcInfoOptional.tcSymbolUsesRev + let tcOpenDeclarationsRev = prevTcInfoOptional.tcOpenDeclarationsRev - // Build symbol keys - let itemKeyStore, semanticClassification = - if enableBackgroundItemKeyStoreAndSemanticClassification then - Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - let sResolutions = sink.GetResolutions() - let builder = ItemKeyStoreBuilder() - let preventDuplicates = HashSet({ new IEqualityComparer with - member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 - member _.GetHashCode o = o.GetHashCode() }) - sResolutions.CapturedNameResolutions - |> Seq.iter (fun cnr -> - let r = cnr.Range - if preventDuplicates.Add struct(r.Start, r.End) then - builder.Write(cnr.Range, cnr.Item)) - - let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcGlobals, tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) - Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification - res - else - None, [||] - - let tcInfoOptional = - { - latestImplFile = if keepAssemblyContents then implFile else None - tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev - tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev - tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: tcOpenDeclarationsRev - itemKeyStore = itemKeyStore - semanticClassification = semanticClassification - } - - return FullState(tcInfo, tcInfoOptional) + // Build symbol keys + let itemKeyStore, semanticClassification = + if enableBackgroundItemKeyStoreAndSemanticClassification then + Logger.LogBlockMessageStart filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + let sResolutions = sink.GetResolutions() + let builder = ItemKeyStoreBuilder() + let preventDuplicates = HashSet({ new IEqualityComparer with + member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Range.posEq s1 s2 && Range.posEq e1 e2 + member _.GetHashCode o = o.GetHashCode() }) + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + if preventDuplicates.Add struct(r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + let res = builder.TryBuildAndReset(), sResolutions.GetSemanticClassification(tcGlobals, tcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) + Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_CreateItemKeyStoreAndSemanticClassification + res + else + None, [||] + + let tcInfoOptional = + { + latestImplFile = if keepAssemblyContents then implFile else None + tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: tcResolutionsRev + tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev + tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: tcOpenDeclarationsRev + itemKeyStore = itemKeyStore + semanticClassification = semanticClassification + } + + return FullState(tcInfo, tcInfoOptional) - } + } - // Run part of the Eventually<_> computation until a timeout is reached. If not complete, - // return a new Eventually<_> computation which recursively runs more of the computation. - // - When the whole thing is finished commit the error results sent through the errorLogger. - // - Each time we do real work we reinstall the CompilationGlobalsScope - let timeSlicedComputation = - fullComputation |> - Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled - maxTimeShareMilliseconds - CancellationToken.None - (fun ctok f -> - // Reinstall the compilation globals each time we start or restart - use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck) - f ctok) - return! timeSlicedComputation - | _ -> - match! prevTcInfoOptional with - | Some prevTcInfoOptional -> - return FullState(prevTcInfo, prevTcInfoOptional) - | _ -> - return MinimumState prevTcInfo + // Run part of the Eventually<_> computation until a timeout is reached. If not complete, + // return a new Eventually<_> computation which recursively runs more of the computation. + // - When the whole thing is finished commit the error results sent through the errorLogger. + // - Each time we do real work we reinstall the CompilationGlobalsScope + let timeSlicedComputation = + fullComputation |> + Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled + maxTimeShareMilliseconds + CancellationToken.None + (fun ctok f -> + // Reinstall the compilation globals each time we start or restart + use unwind = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck) + f ctok) + return! timeSlicedComputation + | _ -> + return! defaultTypeCheck () } /// Global service state @@ -1446,7 +1487,6 @@ type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, tcState: member __.HasAnyFSharpSignatureDataAttribute = true member __.HasMatchingFSharpSignatureDataAttribute _ilg = true - /// Manages an incremental build graph for the build of a single F# project type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInputs, nonFrameworkResolutions, unresolvedReferences, tcConfig: TcConfig, projectDirectory, outfile, assemblyName, niceNameGen: NiceNameGenerator, lexResourceManager, @@ -1513,22 +1553,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput /// Parse the given file and return the given input. let ParseTask ctok (sourceRange: range, filename: string, isLastCompiland) = DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - - let errorLogger = CompilationErrorLogger("ParseTask", tcConfig.errorSeverityOptions) - // Return the disposable object that cleans up - use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) - - try - IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed filename) - let input = ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) - fileParsed.Trigger filename - - input, sourceRange, filename, errorLogger.GetErrors () - with exn -> - let msg = sprintf "unexpected failure in IncrementalFSharpBuild.Parse\nerror = %s" (exn.ToString()) - System.Diagnostics.Debug.Assert(false, msg) - failwith msg - + SyntaxTreeAccumulator(tcConfig, fileParsed, lexResourceManager, sourceRange, filename, isLastCompiland) /// This is a build task function that gets placed into the build rules as the computation for a Vector.Stamp /// @@ -1621,15 +1646,15 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking, - beforeFileChecked, fileChecked, tcAccMinState, Eventually.Done (Some tcAccFullState), (None, range0, String.Empty, [||])) } + beforeFileChecked, fileChecked, tcAccMinState, Eventually.Done (Some tcAccFullState), None) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// /// Type check all files. - let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) input: Eventually = + let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) syntaxTree: Eventually = eventually { RequireCompilationThread ctok - return! tcAcc.Next(input) + return! tcAcc.Next(syntaxTree) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex @@ -1903,7 +1928,8 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput | None -> return! failwith "Build was not evaluated, expected the results to be ready after 'Eval' (GetParseResultsForFile)." } // re-parse on demand instead of retaining - return ParseTask ctok results + let syntaxTree = ParseTask ctok results + return syntaxTree.Parse () } member __.SourceFiles = sourceFiles |> List.map (fun (_, f, _) -> f) diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index a200c265924..b65ead5d9fa 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -21481,7 +21481,7 @@ FSharp.Compiler.SourceCodeServices.FSharpCheckProjectResults: System.String[] De FSharp.Compiler.SourceCodeServices.FSharpCheckProjectResults: System.String[] get_DependencyFiles() FSharp.Compiler.SourceCodeServices.FSharpChecker: Boolean ImplicitlyStartBackgroundWork FSharp.Compiler.SourceCodeServices.FSharpChecker: Boolean get_ImplicitlyStartBackgroundWork() -FSharp.Compiler.SourceCodeServices.FSharpChecker: FSharp.Compiler.SourceCodeServices.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.ReferenceResolver+Resolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.SourceCodeServices.FSharpChecker: FSharp.Compiler.SourceCodeServices.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.ReferenceResolver+Resolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.SourceCodeServices.FSharpChecker: FSharp.Compiler.SourceCodeServices.FSharpChecker Instance FSharp.Compiler.SourceCodeServices.FSharpChecker: FSharp.Compiler.SourceCodeServices.FSharpChecker get_Instance() FSharp.Compiler.SourceCodeServices.FSharpChecker: FSharp.Compiler.SourceCodeServices.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Object]) From 29d25f2cec34475be6c84f4d146f15398ce630f6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 30 Sep 2020 20:50:48 -0700 Subject: [PATCH 21/34] prevent parsing impl files if possible --- src/fsharp/service/IncrementalBuild.fs | 66 ++++++++++++++++++++----- src/fsharp/service/IncrementalBuild.fsi | 2 + 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 8be1817e488..d2462369a29 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -31,6 +31,7 @@ open FSharp.Compiler.TypedTreeOps open Microsoft.DotNet.DependencyManager +open Internal.Utilities open Internal.Utilities.Collections [] @@ -1040,6 +1041,8 @@ type TcInfo = tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list tcDependencyFiles: string list + + sigNameOpt: (string * SyntaxTree.QualifiedNameOfFile) option } member x.TcErrors = @@ -1087,18 +1090,38 @@ type SyntaxTreeAccumulator (tcConfig: TcConfig, fileParsed: Event, lexRe let mutable weakCache: WeakReference<_> option = None - let parse () = + let parse(sigNameOpt: SyntaxTree.QualifiedNameOfFile option) = let errorLogger = CompilationErrorLogger("Parse", tcConfig.errorSeverityOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) try IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed filename) - let input = ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) - fileParsed.Trigger filename + let lower = String.lowercase filename + let canSkip = sigNameOpt.IsSome && FSharpImplFileSuffixes |> List.exists (Filename.checkSuffix lower) + let input = + if canSkip then + SyntaxTree.ParsedInput.ImplFile( + SyntaxTree.ParsedImplFileInput( + filename, + false, + sigNameOpt.Value, + [], + [], + [], + isLastCompiland + ) + ) |> Some + else + ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) + + if not canSkip then + fileParsed.Trigger filename let res = input, sourceRange, filename, errorLogger.GetErrors () - weakCache <- Some(WeakReference<_>(res)) + // If we do not skip parsing the file, then we can cache the real result. + if not canSkip then + weakCache <- Some(WeakReference<_>(res)) res with exn -> let msg = sprintf "unexpected failure in SyntaxTree.parse\nerror = %s" (exn.ToString()) @@ -1106,13 +1129,15 @@ type SyntaxTreeAccumulator (tcConfig: TcConfig, fileParsed: Event, lexRe failwith msg /// Parse the given file and return the given input. - member _.Parse () = + member _.Parse sigNameOpt = match weakCache with | Some weakCache -> match weakCache.TryGetTarget() with | true, res -> res - | _ -> parse () - | _ -> parse () + | _ -> parse sigNameOpt + | _ -> parse sigNameOpt + + member _.FileName = filename /// Accumulated results of type checking. [] @@ -1256,7 +1281,17 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, match syntaxTreeOpt with | None -> return! defaultTypeCheck () | Some syntaxTree -> - match syntaxTree.Parse () with + let sigNameOpt = + if quickCheck then + let sigFileName = Path.ChangeExtension(syntaxTree.FileName, ".fsi") + match prevTcInfo.sigNameOpt with + | Some (expectedSigFileName, sigName) when String.Equals(expectedSigFileName, sigFileName, StringComparison.OrdinalIgnoreCase) -> + Some sigName + | _ -> + None + else + None + match syntaxTree.Parse sigNameOpt with | Some input, _sourceRange, filename, parseErrors -> IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) let capturingErrorLogger = CompilationErrorLogger("TypeCheck", tcConfig.errorSeverityOptions) @@ -1300,6 +1335,12 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, tcErrorsRev = newErrors :: prevTcErrorsRev topAttribs = Some topAttribs tcDependencyFiles = filename :: prevTcDependencyFiles + sigNameOpt = + match input with + | SyntaxTree.ParsedInput.SigFile(SyntaxTree.ParsedSigFileInput(fileName=fileName;qualifiedNameOfFile=qualName)) -> + Some(fileName, qualName) + | _ -> + None } if quickCheck then @@ -1616,7 +1657,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput yield err, (if isError then FSharpErrorSeverity.Error else FSharpErrorSeverity.Warning) ] let initialErrors = Array.append (Array.ofList loadClosureErrors) (errorLogger.GetErrors()) - let tcAccMinState = + let tcInfo = { tcState=tcState tcEnvAtEndOfFile=tcInitial @@ -1625,8 +1666,9 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tcErrorsRev = [ initialErrors ] moduleNamesDict = Map.empty tcDependencyFiles = basicDependencies + sigNameOpt = None } - let tcAccFullState = + let tcInfoOptional = { tcResolutionsRev=[] tcSymbolUsesRev=[] @@ -1646,7 +1688,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking, - beforeFileChecked, fileChecked, tcAccMinState, Eventually.Done (Some tcAccFullState), None) } + beforeFileChecked, fileChecked, tcInfo, Eventually.Done (Some tcInfoOptional), None) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// @@ -1929,7 +1971,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput } // re-parse on demand instead of retaining let syntaxTree = ParseTask ctok results - return syntaxTree.Parse () + return syntaxTree.Parse None } member __.SourceFiles = sourceFiles |> List.map (fun (_, f, _) -> f) diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 4180f53ae59..38caa8f2282 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -60,6 +60,8 @@ type internal TcInfo = tcErrorsRev:(PhasedDiagnostic * FSharpErrorSeverity)[] list tcDependencyFiles: string list + + sigNameOpt: (string * SyntaxTree.QualifiedNameOfFile) option } member TcErrors: (PhasedDiagnostic * FSharpErrorSeverity)[] From be77e72ebe63778e8f8b63042553135622f629e5 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 00:07:11 -0700 Subject: [PATCH 22/34] Trying to fix tests --- src/fsharp/service/IncrementalBuild.fs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index d2462369a29..563f90f800d 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1115,8 +1115,7 @@ type SyntaxTreeAccumulator (tcConfig: TcConfig, fileParsed: Event, lexRe else ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) - if not canSkip then - fileParsed.Trigger filename + fileParsed.Trigger filename let res = input, sourceRange, filename, errorLogger.GetErrors () // If we do not skip parsing the file, then we can cache the real result. @@ -1147,7 +1146,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, beforeFileChecked: Event, fileChecked: Event, prevTcInfo: TcInfo, @@ -1174,7 +1173,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, member this.GetState(quickCheck: bool) = let quickCheck = // Only enable quick checks if we have enabled lazy type checking. - if enableLazyTypeChecking then quickCheck + if enablePartialTypeChecking then quickCheck else false let mustCheck = @@ -1215,7 +1214,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, Some syntaxTree) } @@ -1242,7 +1241,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, None) } @@ -1535,7 +1534,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput keepAssemblyContents, keepAllBackgroundResolutions, maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, dependencyProviderOpt: DependencyProvider option) = let tcConfigP = TcConfigProvider.Constant tcConfig @@ -1687,7 +1686,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, beforeFileChecked, fileChecked, tcInfo, Eventually.Done (Some tcInfoOptional), None) } /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft @@ -1696,7 +1695,9 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) syntaxTree: Eventually = eventually { RequireCompilationThread ctok - return! tcAcc.Next(syntaxTree) + let! nextTcAcc = tcAcc.Next(syntaxTree) + let! _ = nextTcAcc.TypeCheck(if enablePartialTypeChecking then true else false) // Eagerly type check + return nextTcAcc } /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex @@ -1990,7 +1991,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking: bool, + enablePartialTypeChecking: bool, dependencyProviderOpt) = let useSimpleResolutionSwitch = "--simpleresolution" @@ -2116,7 +2117,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, dependencyProviderOpt) return Some builder with e -> From 6d29b0952d0b3b8204a52bb84da3ec2f687821e6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 10:52:47 -0700 Subject: [PATCH 23/34] Refactor --- src/fsharp/service/IncrementalBuild.fs | 255 +++++++++--------- src/fsharp/service/IncrementalBuild.fsi | 2 +- src/fsharp/service/service.fs | 6 +- src/fsharp/service/service.fsi | 4 +- .../LanguageService/FSharpCheckerProvider.fs | 2 +- 5 files changed, 136 insertions(+), 133 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 563f90f800d..2dc958eefd3 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1023,6 +1023,62 @@ module IncrementalBuilderEventTesting = module Tc = FSharp.Compiler.TypeChecker +[] +module IncrementalBuildSyntaxTree = + + [] + type SyntaxTree (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, filename: string, isLastCompiland) = + + let mutable weakCache: WeakReference<_> option = None + + let parse(sigNameOpt: SyntaxTree.QualifiedNameOfFile option) = + let errorLogger = CompilationErrorLogger("Parse", tcConfig.errorSeverityOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) + + try + IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed filename) + let lower = String.lowercase filename + let canSkip = sigNameOpt.IsSome && FSharpImplFileSuffixes |> List.exists (Filename.checkSuffix lower) + let input = + if canSkip then + SyntaxTree.ParsedInput.ImplFile( + SyntaxTree.ParsedImplFileInput( + filename, + false, + sigNameOpt.Value, + [], + [], + [], + isLastCompiland + ) + ) |> Some + else + ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) + + fileParsed.Trigger filename + + let res = input, sourceRange, filename, errorLogger.GetErrors () + // If we do not skip parsing the file, then we can cache the real result. + if not canSkip then + weakCache <- Some(WeakReference<_>(res)) + res + with exn -> + let msg = sprintf "unexpected failure in SyntaxTree.parse\nerror = %s" (exn.ToString()) + System.Diagnostics.Debug.Assert(false, msg) + failwith msg + + /// Parse the given file and return the given input. + member _.Parse sigNameOpt = + match weakCache with + | Some weakCache -> + match weakCache.TryGetTarget() with + | true, res -> res + | _ -> parse sigNameOpt + | _ -> parse sigNameOpt + + member _.FileName = filename + /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] type TcInfo = @@ -1077,70 +1133,17 @@ type TcInfoOptional = /// Accumulated results of type checking. [] type TcInfoState = - | MinimumState of TcInfo + | PartialState of TcInfo | FullState of TcInfo * TcInfoOptional member this.Minimum = match this with - | MinimumState tcInfo -> tcInfo + | PartialState tcInfo -> tcInfo | FullState(tcInfo, _) -> tcInfo -[] -type SyntaxTreeAccumulator (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, filename: string, isLastCompiland) = - - let mutable weakCache: WeakReference<_> option = None - - let parse(sigNameOpt: SyntaxTree.QualifiedNameOfFile option) = - let errorLogger = CompilationErrorLogger("Parse", tcConfig.errorSeverityOptions) - // Return the disposable object that cleans up - use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) - - try - IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed filename) - let lower = String.lowercase filename - let canSkip = sigNameOpt.IsSome && FSharpImplFileSuffixes |> List.exists (Filename.checkSuffix lower) - let input = - if canSkip then - SyntaxTree.ParsedInput.ImplFile( - SyntaxTree.ParsedImplFileInput( - filename, - false, - sigNameOpt.Value, - [], - [], - [], - isLastCompiland - ) - ) |> Some - else - ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) - - fileParsed.Trigger filename - - let res = input, sourceRange, filename, errorLogger.GetErrors () - // If we do not skip parsing the file, then we can cache the real result. - if not canSkip then - weakCache <- Some(WeakReference<_>(res)) - res - with exn -> - let msg = sprintf "unexpected failure in SyntaxTree.parse\nerror = %s" (exn.ToString()) - System.Diagnostics.Debug.Assert(false, msg) - failwith msg - - /// Parse the given file and return the given input. - member _.Parse sigNameOpt = - match weakCache with - | Some weakCache -> - match weakCache.TryGetTarget() with - | true, res -> res - | _ -> parse sigNameOpt - | _ -> parse sigNameOpt - - member _.FileName = filename - -/// Accumulated results of type checking. +/// Semantic model of an underlying syntax tree. [] -type TypeCheckAccumulator ( tcConfig: TcConfig, +type SemanticModel ( tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, keepAssemblyContents, keepAllBackgroundResolutions, @@ -1151,17 +1154,17 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, fileChecked: Event, prevTcInfo: TcInfo, prevTcInfoOptional: Eventually, - syntaxTreeOpt: SyntaxTreeAccumulator option) = + syntaxTreeOpt: SyntaxTree option) = let lazyTcInfoState: TcInfoState option ref = ref None let defaultTypeCheck () = eventually { - match! prevTcInfoOptional with - | Some prevTcInfoOptional -> + match prevTcInfoOptional with + | Eventually.Done(Some prevTcInfoOptional) -> return FullState(prevTcInfo, prevTcInfoOptional) | _ -> - return MinimumState prevTcInfo + return PartialState prevTcInfo } member _.TcConfig = tcConfig @@ -1170,16 +1173,16 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, member _.TcImports = tcImports - member this.GetState(quickCheck: bool) = - let quickCheck = - // Only enable quick checks if we have enabled lazy type checking. - if enablePartialTypeChecking then quickCheck + member this.GetState(partialCheck: bool) = + let partialCheck = + // Only enable partial checks if we have enabled lazy type checking. + if enablePartialTypeChecking then partialCheck else false let mustCheck = - match !lazyTcInfoState, quickCheck with + match !lazyTcInfoState, partialCheck with | None, _ -> true - | Some(MinimumState _), false -> true + | Some(PartialState _), false -> true | _ -> false if mustCheck then @@ -1189,7 +1192,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, | Some tcInfoState -> tcInfoState |> Eventually.Done | _ -> eventually { - let! tcInfoState = this.TypeCheck(quickCheck) + let! tcInfoState = this.TypeCheck(partialCheck) lazyTcInfoState := Some tcInfoState return tcInfoState } @@ -1205,7 +1208,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, | _ -> return None } return - TypeCheckAccumulator( + SemanticModel( tcConfig, tcGlobals, tcImports, @@ -1232,7 +1235,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, } return - TypeCheckAccumulator( + SemanticModel( tcConfig, tcGlobals, tcImports, @@ -1256,7 +1259,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, let! state = this.GetState(false) match state with | FullState(tcInfo, tcInfoOptional) -> return tcInfo, tcInfoOptional - | MinimumState tcInfo -> + | PartialState tcInfo -> return tcInfo, { @@ -1269,9 +1272,9 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, } } - member _.TypeCheck (quickCheck: bool) = - match quickCheck, !lazyTcInfoState with - | true, Some (MinimumState _ as state) + member private _.TypeCheck (partialCheck: bool) = + match partialCheck, !lazyTcInfoState with + | true, Some (PartialState _ as state) | true, Some (FullState _ as state) -> state |> Eventually.Done | false, Some (FullState _ as state) -> state |> Eventually.Done | _ -> @@ -1281,7 +1284,7 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, | None -> return! defaultTypeCheck () | Some syntaxTree -> let sigNameOpt = - if quickCheck then + if partialCheck then let sigFileName = Path.ChangeExtension(syntaxTree.FileName, ".fsi") match prevTcInfo.sigNameOpt with | Some (expectedSigFileName, sigName) when String.Equals(expectedSigFileName, sigFileName, StringComparison.OrdinalIgnoreCase) -> @@ -1315,9 +1318,9 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, tcConfig, tcImports, tcGlobals, None, - (if quickCheck then TcResultsSink.NoSink else TcResultsSink.WithSink sink), + (if partialCheck then TcResultsSink.NoSink else TcResultsSink.WithSink sink), prevTcState, input, - quickCheck) + partialCheck) Logger.LogBlockMessageStop filename LogCompilerFunctionId.IncrementalBuild_TypeCheck fileChecked.Trigger filename @@ -1342,11 +1345,11 @@ type TypeCheckAccumulator ( tcConfig: TcConfig, None } - if quickCheck then - return MinimumState tcInfo + if partialCheck then + return PartialState tcInfo else match! prevTcInfoOptional with - | None -> return MinimumState tcInfo + | None -> return PartialState tcInfo | Some prevTcInfoOptional -> /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away let tcResolutionsRev = prevTcInfoOptional.tcResolutionsRev @@ -1467,25 +1470,25 @@ type FrameworkImportsCache(keepStrongly) = /// Represents the interim state of checking an assembly [] -type PartialCheckResults private (tcAcc: TypeCheckAccumulator, timeStamp: DateTime) = +type PartialCheckResults private (semanticModel: SemanticModel, timeStamp: DateTime) = let eval ctok (work: Eventually<'T>) = match work with | Eventually.Done res -> res | _ -> Eventually.force ctok work - member _.TcImports = tcAcc.TcImports - member _.TcGlobals = tcAcc.TcGlobals - member _.TcConfig = tcAcc.TcConfig + member _.TcImports = semanticModel.TcImports + member _.TcGlobals = semanticModel.TcGlobals + member _.TcConfig = semanticModel.TcConfig member _.TimeStamp = timeStamp - member _.TcInfo ctok = tcAcc.TcInfo |> eval ctok + member _.TcInfo ctok = semanticModel.TcInfo |> eval ctok - member _.TcInfoFull ctok = tcAcc.TcInfoFull |> eval ctok + member _.TcInfoFull ctok = semanticModel.TcInfoFull |> eval ctok - static member Create (tcAcc: TypeCheckAccumulator, timestamp) = - PartialCheckResults(tcAcc, timestamp) + static member Create (semanticModel: SemanticModel, timestamp) = + PartialCheckResults(semanticModel, timestamp) [] module Utilities = @@ -1593,7 +1596,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput /// Parse the given file and return the given input. let ParseTask ctok (sourceRange: range, filename: string, isLastCompiland) = DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - SyntaxTreeAccumulator(tcConfig, fileParsed, lexResourceManager, sourceRange, filename, isLastCompiland) + SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, filename, isLastCompiland) /// This is a build task function that gets placed into the build rules as the computation for a Vector.Stamp /// @@ -1605,7 +1608,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex /// // Link all the assemblies together and produce the input typecheck accumulator - let CombineImportedAssembliesTask ctok _ : Cancellable = + let CombineImportedAssembliesTask ctok _ : Cancellable = cancellable { let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig.errorSeverityOptions) // Return the disposable object that cleans up @@ -1677,7 +1680,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput semanticClassification = [||] } return - TypeCheckAccumulator( + SemanticModel( tcConfig, tcGlobals, tcImports, @@ -1692,18 +1695,18 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput /// This is a build task function that gets placed into the build rules as the computation for a Vector.ScanLeft /// /// Type check all files. - let TypeCheckTask ctok (tcAcc: TypeCheckAccumulator) syntaxTree: Eventually = + let TypeCheckTask ctok (prevSemanticModel: SemanticModel) syntaxTree: Eventually = eventually { RequireCompilationThread ctok - let! nextTcAcc = tcAcc.Next(syntaxTree) - let! _ = nextTcAcc.TypeCheck(if enablePartialTypeChecking then true else false) // Eagerly type check - return nextTcAcc + let! semanticModel = prevSemanticModel.Next(syntaxTree) + let! _ = semanticModel.GetState(if enablePartialTypeChecking then true else false) // Eagerly type check + return semanticModel } /// This is a build task function that gets placed into the build rules as the computation for a Vector.Demultiplex /// /// Finish up the typechecking to produce outputs for the rest of the compilation process - let FinalizeTypeCheckTask ctok (tcStates: TypeCheckAccumulator[]) = + let FinalizeTypeCheckTask ctok (semanticModels: SemanticModel[]) = cancellable { DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok @@ -1711,17 +1714,17 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.TypeCheck) // Get the state at the end of the type-checking of the last file - let finalAcc = tcStates.[tcStates.Length-1] + let finalSemanticModel = semanticModels.[semanticModels.Length-1] - let finalInfo = finalAcc.TcInfo |> Eventually.force ctok + let finalInfo = finalSemanticModel.TcInfo |> Eventually.force ctok // Finish the checking let (_tcEnvAtEndOfLastFile, topAttrs, mimpls, _), tcState = let results = - tcStates + semanticModels |> List.ofArray - |> List.map (fun acc -> - let tcInfo, tcInfoOptional = acc.TcInfoFull |> Eventually.force ctok + |> List.map (fun semanticModel -> + let tcInfo, tcInfoOptional = semanticModel.TcInfoFull |> Eventually.force ctok tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, tcInfoOptional.latestImplFile, tcInfo.latestCcuSigForFile) TypeCheckMultipleInputsFinish (results, finalInfo.tcState) @@ -1780,8 +1783,8 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput errorRecoveryNoRange e mkSimpleAssemblyRef assemblyName, None, None - let finalAccWithErrors = finalAcc.Finish((errorLogger.GetErrors() :: finalInfo.tcErrorsRev), Some topAttrs) |> Eventually.force ctok - return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalAccWithErrors + let finalSemanticModelWithErrors = finalSemanticModel.Finish((errorLogger.GetErrors() :: finalInfo.tcErrorsRev), Some topAttrs) |> Eventually.force ctok + return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalSemanticModelWithErrors } // END OF BUILD TASK FUNCTIONS @@ -1797,18 +1800,18 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput // Build let stampedFileNamesNode = Vector.Stamp "SourceFileTimeStamps" StampFileNameTask fileNamesNode let stampedReferencedAssembliesNode = Vector.Stamp "StampReferencedAssembly" StampReferencedAssemblyTask referencedAssembliesNode - let initialTcAccNode = Vector.Demultiplex "CombineImportedAssemblies" CombineImportedAssembliesTask stampedReferencedAssembliesNode - let tcStatesNode = Vector.ScanLeft "TypeCheckingStates" (fun ctok tcAcc n -> TypeCheckTask ctok tcAcc (ParseTask ctok n)) initialTcAccNode stampedFileNamesNode - let finalizedTypeCheckNode = Vector.Demultiplex "FinalizeTypeCheck" FinalizeTypeCheckTask tcStatesNode + let initialSemanticModelNode = Vector.Demultiplex "CombineImportedAssemblies" CombineImportedAssembliesTask stampedReferencedAssembliesNode + let semanticModelNodes = Vector.ScanLeft "TypeCheckingStates" (fun ctok semanticModel n -> TypeCheckTask ctok semanticModel (ParseTask ctok n)) initialSemanticModelNode stampedFileNamesNode + let finalizedSemanticModelNode = Vector.Demultiplex "FinalizeTypeCheck" FinalizeTypeCheckTask semanticModelNodes // Outputs let buildDescription = new BuildDescriptionScope () do buildDescription.DeclareVectorOutput stampedFileNamesNode do buildDescription.DeclareVectorOutput stampedReferencedAssembliesNode - do buildDescription.DeclareVectorOutput tcStatesNode - do buildDescription.DeclareScalarOutput initialTcAccNode - do buildDescription.DeclareScalarOutput finalizedTypeCheckNode + do buildDescription.DeclareVectorOutput semanticModelNodes + do buildDescription.DeclareScalarOutput initialSemanticModelNode + do buildDescription.DeclareScalarOutput finalizedSemanticModelNode // END OF BUILD DESCRIPTION // --------------------------------------------------------------------------------------------- @@ -1849,7 +1852,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput member __.Step (ctok: CompilationThreadToken) = cancellable { let cache = TimeStampCache defaultTimeStamp // One per step - let! res = IncrementalBuild.Step cache ctok SavePartialBuild (Target(tcStatesNode, None)) partialBuild + let! res = IncrementalBuild.Step cache ctok SavePartialBuild (Target(semanticModelNodes, None)) partialBuild match res with | None -> projectChecked.Trigger() @@ -1862,11 +1865,11 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let slotOfFile = builder.GetSlotOfFileName filename let result = match slotOfFile with - | (*first file*) 0 -> GetScalarResult(initialTcAccNode, partialBuild) - | _ -> GetVectorResultBySlot(tcStatesNode, slotOfFile-1, partialBuild) + | (*first file*) 0 -> GetScalarResult(initialSemanticModelNode, partialBuild) + | _ -> GetVectorResultBySlot(semanticModelNodes, slotOfFile-1, partialBuild) match result with - | Some (tcAcc, timestamp) -> Some (PartialCheckResults.Create (tcAcc, timestamp)) + | Some (semanticModel, timestamp) -> Some (PartialCheckResults.Create (semanticModel, timestamp)) | _ -> None @@ -1874,8 +1877,8 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let slotOfFile = builder.GetSlotOfFileName filename let cache = TimeStampCache defaultTimeStamp match slotOfFile with - | (*first file*) 0 -> IncrementalBuild.IsReady cache (Target(initialTcAccNode, None)) partialBuild - | _ -> IncrementalBuild.IsReady cache (Target(tcStatesNode, Some (slotOfFile-1))) partialBuild + | (*first file*) 0 -> IncrementalBuild.IsReady cache (Target(initialSemanticModelNode, None)) partialBuild + | _ -> IncrementalBuild.IsReady cache (Target(semanticModelNodes, Some (slotOfFile-1))) partialBuild member __.GetCheckResultsBeforeSlotInProject (ctok: CompilationThreadToken, slotOfFile) = cancellable { @@ -1884,15 +1887,15 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput cancellable { match slotOfFile with | (*first file*) 0 -> - let! build = IncrementalBuild.Eval cache ctok SavePartialBuild initialTcAccNode partialBuild - return GetScalarResult(initialTcAccNode, build) + let! build = IncrementalBuild.Eval cache ctok SavePartialBuild initialSemanticModelNode partialBuild + return GetScalarResult(initialSemanticModelNode, build) | _ -> - let! build = IncrementalBuild.EvalUpTo cache ctok SavePartialBuild (tcStatesNode, (slotOfFile-1)) partialBuild - return GetVectorResultBySlot(tcStatesNode, slotOfFile-1, build) + let! build = IncrementalBuild.EvalUpTo cache ctok SavePartialBuild (semanticModelNodes, (slotOfFile-1)) partialBuild + return GetVectorResultBySlot(semanticModelNodes, slotOfFile-1, build) } match result with - | Some (tcAcc, timestamp) -> return PartialCheckResults.Create (tcAcc, timestamp) + | Some (semanticModel, timestamp) -> return PartialCheckResults.Create (semanticModel, timestamp) | None -> return! failwith "Build was not evaluated, expected the results to be ready after 'Eval' (GetCheckResultsBeforeSlotInProject)." } @@ -1910,14 +1913,14 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput member __.GetCheckResultsAndImplementationsForProject(ctok: CompilationThreadToken) = cancellable { let cache = TimeStampCache defaultTimeStamp - let! build = IncrementalBuild.Eval cache ctok SavePartialBuild finalizedTypeCheckNode partialBuild - match GetScalarResult(finalizedTypeCheckNode, build) with - | Some ((ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, tcAcc), timestamp) -> - return PartialCheckResults.Create (tcAcc, timestamp), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt + let! build = IncrementalBuild.Eval cache ctok SavePartialBuild finalizedSemanticModelNode partialBuild + match GetScalarResult(finalizedSemanticModelNode, build) with + | Some ((ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, semanticModel), timestamp) -> + return PartialCheckResults.Create (semanticModel, timestamp), ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt | None -> // helpers to diagnose https://github.com/Microsoft/visualfsharp/pull/2460/ - let brname = match GetTopLevelExprByName(build, finalizedTypeCheckNode.Name) with ScalarBuildRule se ->se.Id | _ -> Id 0xdeadbeef - let data = (finalizedTypeCheckNode.Name, + let brname = match GetTopLevelExprByName(build, finalizedSemanticModelNode.Name) with ScalarBuildRule se ->se.Id | _ -> Id 0xdeadbeef + let data = (finalizedSemanticModelNode.Name, ((build.Results :> IDictionary<_, _>).Keys |> Seq.toArray), brname, build.Results.ContainsKey brname, diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 38caa8f2282..5c3ae0ae21f 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -216,7 +216,7 @@ type internal IncrementalBuilder = suggestNamesForErrors: bool * keepAllBackgroundSymbolUses: bool * enableBackgroundItemKeyStoreAndSemanticClassification: bool * - enableLazyTypeChecking: bool * + enablePartialTypeChecking: bool * dependencyProvider: DependencyProvider option -> Cancellable diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index d45769f7720..a7c7c9e6688 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -1042,7 +1042,7 @@ type FSharpChecker(legacyReferenceResolver, let maxMemEvent = new Event() /// Instantiate an interactive checker. - static member Create(?projectCacheSize, ?keepAssemblyContents, ?keepAllBackgroundResolutions, ?legacyReferenceResolver, ?tryGetMetadataSnapshot, ?suggestNamesForErrors, ?keepAllBackgroundSymbolUses, ?enableBackgroundItemKeyStoreAndSemanticClassification, ?enableLazyTypeChecking) = + static member Create(?projectCacheSize, ?keepAssemblyContents, ?keepAllBackgroundResolutions, ?legacyReferenceResolver, ?tryGetMetadataSnapshot, ?suggestNamesForErrors, ?keepAllBackgroundSymbolUses, ?enableBackgroundItemKeyStoreAndSemanticClassification, ?enablePartialTypeChecking) = let legacyReferenceResolver = match legacyReferenceResolver with @@ -1056,8 +1056,8 @@ type FSharpChecker(legacyReferenceResolver, let suggestNamesForErrors = defaultArg suggestNamesForErrors false let keepAllBackgroundSymbolUses = defaultArg keepAllBackgroundSymbolUses true let enableBackgroundItemKeyStoreAndSemanticClassification = defaultArg enableBackgroundItemKeyStoreAndSemanticClassification false - let enableLazyTypeChecking = defaultArg enableLazyTypeChecking false - new FSharpChecker(legacyReferenceResolver, projectCacheSizeReal,keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) + let enablePartialTypeChecking = defaultArg enablePartialTypeChecking false + new FSharpChecker(legacyReferenceResolver, projectCacheSizeReal,keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking) member __.ReferenceResolver = legacyReferenceResolver diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index a8b7d0af42a..733e537f480 100755 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -78,11 +78,11 @@ type public FSharpChecker = /// Indicate whether name suggestion should be enabled /// Indicate whether all symbol uses should be kept in background checking /// Indicates whether a table of symbol keys should be kept for background compilation - /// Indicates whether to perform partial type checking. Performance will vary depending on how existing APIs are used. + /// Indicates whether to perform partial type checking. Performance will vary depending on how existing APIs are used. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: ReferenceResolver.Resolver * ?tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * - ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enableLazyTypeChecking: bool + ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enablePartialTypeChecking: bool -> FSharpChecker /// diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 9f32e41b974..05d7f9b1309 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -61,7 +61,7 @@ type internal FSharpCheckerProvider tryGetMetadataSnapshot = tryGetMetadataSnapshot, keepAllBackgroundSymbolUses = false, enableBackgroundItemKeyStoreAndSemanticClassification = true, - enableLazyTypeChecking = true) + enablePartialTypeChecking = true) // This is one half of the bridge between the F# background builder and the Roslyn analysis engine. // When the F# background builder refreshes the background semantic build context for a file, From ef9149d5fec0b55ed79c2fb5426c00f6f22105d5 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 11:23:06 -0700 Subject: [PATCH 24/34] Throw if enablePartialChecking and keepAssemblyContents are both enabled --- src/fsharp/service/IncrementalBuild.fs | 10 ++++++++-- src/fsharp/service/service.fs | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 2dc958eefd3..6480284c566 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1724,8 +1724,14 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput semanticModels |> List.ofArray |> List.map (fun semanticModel -> - let tcInfo, tcInfoOptional = semanticModel.TcInfoFull |> Eventually.force ctok - tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, tcInfoOptional.latestImplFile, tcInfo.latestCcuSigForFile) + let tcInfo, latestImplFile = + if enablePartialTypeChecking then + let tcInfo = semanticModel.TcInfo |> Eventually.force ctok + tcInfo, None + else + let tcInfo, tcInfoOptional = semanticModel.TcInfoFull |> Eventually.force ctok + tcInfo, tcInfoOptional.latestImplFile + tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, latestImplFile, tcInfo.latestCcuSigForFile) TypeCheckMultipleInputsFinish (results, finalInfo.tcState) let ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index a7c7c9e6688..527eee22e74 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -1057,6 +1057,10 @@ type FSharpChecker(legacyReferenceResolver, let keepAllBackgroundSymbolUses = defaultArg keepAllBackgroundSymbolUses true let enableBackgroundItemKeyStoreAndSemanticClassification = defaultArg enableBackgroundItemKeyStoreAndSemanticClassification false let enablePartialTypeChecking = defaultArg enablePartialTypeChecking false + + if keepAssemblyContents && enablePartialTypeChecking then + invalidArg "enablePartialTypeChecking" "'keepAssemblyContents' and 'enablePartialTypeChecking' cannot be both enabled." + new FSharpChecker(legacyReferenceResolver, projectCacheSizeReal,keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking) member __.ReferenceResolver = legacyReferenceResolver From ab54e62915b3fa2f488f7b92e58519ebaf2c1e22 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 11:43:17 -0700 Subject: [PATCH 25/34] minor comment update --- src/fsharp/service/IncrementalBuild.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 6480284c566..7df533d35ad 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1142,7 +1142,7 @@ type TcInfoState = | FullState(tcInfo, _) -> tcInfo /// Semantic model of an underlying syntax tree. -[] +[] type SemanticModel ( tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, @@ -1175,7 +1175,7 @@ type SemanticModel ( tcConfig: TcConfig, member this.GetState(partialCheck: bool) = let partialCheck = - // Only enable partial checks if we have enabled lazy type checking. + // Only partial check if we have enabled it. if enablePartialTypeChecking then partialCheck else false @@ -1699,7 +1699,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput eventually { RequireCompilationThread ctok let! semanticModel = prevSemanticModel.Next(syntaxTree) - let! _ = semanticModel.GetState(if enablePartialTypeChecking then true else false) // Eagerly type check + let! _ = semanticModel.GetState(enablePartialTypeChecking) // Eagerly type check return semanticModel } From 8f16c458c7abbb90781f6a3715a037ccf31dd7a3 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 11:50:23 -0700 Subject: [PATCH 26/34] minor refactor --- src/fsharp/service/IncrementalBuild.fs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 7df533d35ad..18dd1f4dbb7 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1351,11 +1351,6 @@ type SemanticModel ( tcConfig: TcConfig, match! prevTcInfoOptional with | None -> return PartialState tcInfo | Some prevTcInfoOptional -> - /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away - let tcResolutionsRev = prevTcInfoOptional.tcResolutionsRev - let tcSymbolUsesRev = prevTcInfoOptional.tcSymbolUsesRev - let tcOpenDeclarationsRev = prevTcInfoOptional.tcOpenDeclarationsRev - // Build symbol keys let itemKeyStore, semanticClassification = if enableBackgroundItemKeyStoreAndSemanticClassification then @@ -1379,10 +1374,11 @@ type SemanticModel ( tcConfig: TcConfig, let tcInfoOptional = { + /// 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) :: tcResolutionsRev - tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: tcSymbolUsesRev - tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: tcOpenDeclarationsRev + 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 itemKeyStore = itemKeyStore semanticClassification = semanticClassification } From 84138448927a6188fb14d3d25ba09a820f9298a6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 13:10:35 -0700 Subject: [PATCH 27/34] Trying to pass tests --- src/fsharp/service/IncrementalBuild.fs | 70 ++++++++++++++++++-------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 18dd1f4dbb7..fbee056e345 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1136,14 +1136,14 @@ type TcInfoState = | PartialState of TcInfo | FullState of TcInfo * TcInfoOptional - member this.Minimum = + member this.Partial = match this with | PartialState tcInfo -> tcInfo | FullState(tcInfo, _) -> tcInfo /// Semantic model of an underlying syntax tree. [] -type SemanticModel ( tcConfig: TcConfig, +type SemanticModel private (tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, keepAssemblyContents, keepAllBackgroundResolutions, @@ -1154,9 +1154,8 @@ type SemanticModel ( tcConfig: TcConfig, fileChecked: Event, prevTcInfo: TcInfo, prevTcInfoOptional: Eventually, - syntaxTreeOpt: SyntaxTree option) = - - let lazyTcInfoState: TcInfoState option ref = ref None + syntaxTreeOpt: SyntaxTree option, + lazyTcInfoState: TcInfoState option ref) = let defaultTypeCheck () = eventually { @@ -1200,11 +1199,11 @@ type SemanticModel ( tcConfig: TcConfig, member this.Next(syntaxTree) = eventually { let! prevState = this.GetState(true) - let lazyPrevFullState = + let lazyPrevTcInfoOptional = eventually { let! prevState = this.GetState(false) match prevState with - | FullState(_, prevFullState) -> return Some prevFullState + | FullState(_, prevTcInfoOptional) -> return Some prevTcInfoOptional | _ -> return None } return @@ -1218,21 +1217,23 @@ type SemanticModel ( tcConfig: TcConfig, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - beforeFileChecked, fileChecked, prevState.Minimum, lazyPrevFullState, Some syntaxTree) + beforeFileChecked, + fileChecked, + prevState.Partial, + lazyPrevTcInfoOptional, + Some syntaxTree, + ref None) } member this.Finish(finalTcErrorsRev, finalTopAttribs) = eventually { - let! prevState = this.GetState(true) + let! state = this.GetState(true) - let prevMinState = { prevState.Minimum with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs } - let lazyPrevFullState = - eventually { - let! prevState = this.GetState(false) - match prevState with - | FullState (_, prevFullState) -> return Some prevFullState - | _ -> return None - } + let finishTcInfo = { state.Partial with tcErrorsRev = finalTcErrorsRev; topAttribs = finalTopAttribs } + let finishState = + match state with + | PartialState(_) -> PartialState(finishTcInfo) + | FullState(_, tcInfoOptional) -> FullState(finishTcInfo, tcInfoOptional) return SemanticModel( @@ -1245,13 +1246,18 @@ type SemanticModel ( tcConfig: TcConfig, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking, - beforeFileChecked, fileChecked, prevMinState, lazyPrevFullState, None) + beforeFileChecked, + fileChecked, + prevTcInfo, + prevTcInfoOptional, + syntaxTreeOpt, + ref (Some finishState)) } member this.TcInfo = eventually { let! state = this.GetState(true) - return state.Minimum + return state.Partial } member this.TcInfoFull = @@ -1404,6 +1410,30 @@ type SemanticModel ( tcConfig: TcConfig, | _ -> return! defaultTypeCheck () } + + static member Create(tcConfig: TcConfig, + tcGlobals: TcGlobals, + tcImports: TcImports, + keepAssemblyContents, keepAllBackgroundResolutions, + maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + beforeFileChecked: Event, + fileChecked: Event, + prevTcInfo: TcInfo, + prevTcInfoOptional: Eventually, + syntaxTreeOpt: SyntaxTree option) = + SemanticModel(tcConfig, tcGlobals, tcImports, + keepAssemblyContents, keepAllBackgroundResolutions, + maxTimeShareMilliseconds, keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + beforeFileChecked, + fileChecked, + prevTcInfo, + prevTcInfoOptional, + syntaxTreeOpt, + ref None) /// Global service state type FrameworkImportsCacheKey = (*resolvedpath*)string list * string * (*TargetFrameworkDirectories*)string list * (*fsharpBinaries*)string * (*langVersion*)decimal @@ -1676,7 +1706,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput semanticClassification = [||] } return - SemanticModel( + SemanticModel.Create( tcConfig, tcGlobals, tcImports, From 10879720e06cdd4a1869ea4dee8a02717e2e85e4 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 1 Oct 2020 18:26:58 -0700 Subject: [PATCH 28/34] Only getting assembly data --- src/fsharp/service/service.fs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 527eee22e74..faab1d50653 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -307,8 +307,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member x.EvaluateRawContents(ctok) = cancellable { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckProjectImpl", nm) - let! (r : FSharpCheckProjectResults) = self.ParseAndCheckProjectImpl(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") - return r.RawFSharpAssemblyData + return! self.GetAssemblyData(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") } member x.TryGetLogicalTimeStamp(cache, ctok) = self.TryGetLogicalTimeStampForProject(cache, ctok, opts, userOpName + ".TimeStampReferencedProject("+nm+")") @@ -832,6 +831,17 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcDependencyFiles)) } + member _.GetAssemblyData(options, ctok, userOpName) = + cancellable { + let! builderOpt,_ = getOrCreateBuilder (ctok, options, userOpName) + match builderOpt with + | None -> + return None + | Some builder -> + let! (_, _, tcAssemblyDataOpt, _) = builder.GetCheckResultsAndImplementationsForProject(ctok) + return tcAssemblyDataOpt + } + /// Get the timestamp that would be on the output if fully built immediately member private __.TryGetLogicalTimeStampForProject(cache, ctok, options, userOpName: string) = @@ -1023,9 +1033,9 @@ type FSharpChecker(legacyReferenceResolver, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking) = + enablePartialTypeChecking) = - let backgroundCompiler = BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) + let backgroundCompiler = BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking) static let globalInstance = lazy FSharpChecker.Create() From c48217bef139e65ce41782fde94aeb63a3226b09 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 2 Oct 2020 12:27:06 -0700 Subject: [PATCH 29/34] Remove unnecessary change --- src/fsharp/CompilerConfig.fs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index c3589d1bb15..060d3f864cc 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -605,9 +605,7 @@ type TcConfigBuilder = #endif compilationThread = let ctok = CompilationThreadToken () - { new ICompilationThread with - member __.EnqueueWork work = work ctok - } + { new ICompilationThread with member __.EnqueueWork work = work ctok } pause = false alwaysCallVirt = true noDebugData = false From 0b7ae6cfd5b2cdc30142f591b720da4369ba78c3 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 2 Oct 2020 12:59:15 -0700 Subject: [PATCH 30/34] Added some documentation --- src/fsharp/service/IncrementalBuild.fs | 4 +++- src/fsharp/service/IncrementalBuild.fsi | 2 ++ src/fsharp/service/service.fs | 3 ++- src/fsharp/service/service.fsi | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index fbee056e345..0890828e276 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1725,7 +1725,9 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput eventually { RequireCompilationThread ctok let! semanticModel = prevSemanticModel.Next(syntaxTree) - let! _ = semanticModel.GetState(enablePartialTypeChecking) // Eagerly type check + // Eagerly type check + // We need to do this to keep the expected behavior of events (namely fileChecked) when checking a file/project. + let! _ = semanticModel.GetState(enablePartialTypeChecking) return semanticModel } diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 5c3ae0ae21f..17c6b94ee87 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -105,6 +105,8 @@ type internal PartialCheckResults = member TcInfo: CompilationThreadToken -> TcInfo + /// Can cause a second type-check if `enablePartialTypeChecking` is true in the checker. + /// Only use when it's absolutely necessary to get rich information on a file. member TcInfoFull: CompilationThreadToken -> TcInfo * TcInfoOptional member TimeStamp: DateTime diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index faab1d50653..80d52e78dd5 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -306,7 +306,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC { new IProjectReference with member x.EvaluateRawContents(ctok) = cancellable { - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckProjectImpl", nm) + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) return! self.GetAssemblyData(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") } member x.TryGetLogicalTimeStamp(cache, ctok) = @@ -852,6 +852,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> None | Some builder -> Some (builder.GetLogicalTimeStampForProject(cache, ctok)) + /// Parse and typecheck the whole project. member bc.ParseAndCheckProject(options, userOpName) = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok, userOpName)) diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index 733e537f480..acb6b857ec4 100755 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -78,7 +78,7 @@ type public FSharpChecker = /// Indicate whether name suggestion should be enabled /// Indicate whether all symbol uses should be kept in background checking /// Indicates whether a table of symbol keys should be kept for background compilation - /// Indicates whether to perform partial type checking. Performance will vary depending on how existing APIs are used. + /// Indicates whether to perform partial type checking. Cannot be set to true if keepAssmeblyContents is true. If set to true, can cause duplicate type-checks when richer information on a file is needed, but can skip background type-checking entirely on implementation files with signature files. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: ReferenceResolver.Resolver * ?tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * @@ -216,6 +216,7 @@ type public FSharpChecker = /// /// Parse and typecheck all files in a project. /// All files are read from the FileSystem API + /// Can cause a second type-check on the entire project when `enablePartialTypeChecking` is true on the FSharpChecker. /// /// /// The options for the project or script. @@ -295,6 +296,7 @@ type public FSharpChecker = /// /// Like CheckFileInProject, but uses the existing results from the background builder. /// All files are read from the FileSystem API, including the file being checked. + /// Can cause a second type-check when `enablePartialTypeChecking` is true on the FSharpChecker. /// /// /// The filename for the file. @@ -305,6 +307,7 @@ type public FSharpChecker = /// /// Optimized find references for a given symbol in a file of project. /// All files are read from the FileSystem API, including the file being checked. + /// Can cause a second type-check when `enablePartialTypeChecking` is true on the FSharpChecker. /// /// /// The filename for the file. @@ -317,6 +320,7 @@ type public FSharpChecker = /// /// Get semantic classification for a file. /// All files are read from the FileSystem API, including the file being checked. + /// Can cause a second type-check when `enablePartialTypeChecking` is true on the FSharpChecker. /// /// /// The filename for the file. From 36dcf61cdb8c86f39a38633c6cc345fab1fa72d1 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 2 Oct 2020 13:02:23 -0700 Subject: [PATCH 31/34] Minor comment update --- src/fsharp/service/IncrementalBuild.fs | 2 +- src/fsharp/service/IncrementalBuild.fsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 0890828e276..15902648e51 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1104,7 +1104,7 @@ type TcInfo = member x.TcErrors = Array.concat (List.rev x.tcErrorsRev) -/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for tooling. +/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for in tooling. [] type TcInfoOptional = { diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 17c6b94ee87..7537f1e3c81 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -66,7 +66,7 @@ type internal TcInfo = member TcErrors: (PhasedDiagnostic * FSharpErrorSeverity)[] -/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for tooling. +/// Accumulated results of type checking. Optional data that isn't needed to type-check a file, but needed for more information for in tooling. [] type internal TcInfoOptional = { From 2db6f8d6cdc9ad8dead896e5293c6318ee5529e9 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 2 Oct 2020 13:31:37 -0700 Subject: [PATCH 32/34] Renamed quickCheck to skipImplIfSigExists --- src/fsharp/ParseAndCheckInputs.fs | 4 ++-- src/fsharp/ParseAndCheckInputs.fsi | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 1ff6fb366b7..5c31ae52f5e 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -621,7 +621,7 @@ let GetInitialTcState(m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcIm tcsCcuSig = Construct.NewEmptyModuleOrNamespaceType Namespace } /// Typecheck a single file (or interactive entry into F# Interactive) -let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput, quickCheck: bool) = +let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState: TcState, inp: ParsedInput, skipImplIfSigExists: bool) = eventually { try @@ -690,7 +690,7 @@ let TypeCheckOneInputEventually (checkForErrors, tcConfig: TcConfig, tcImports: // Typecheck the implementation file let typeCheckOne = - if quickCheck && hadSig then + if skipImplIfSigExists && hadSig then let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(rootSigOpt.Value, ModuleOrNamespaceExpr.TMDefs [], range.Zero) let dummyImplFile = TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap []) diff --git a/src/fsharp/ParseAndCheckInputs.fsi b/src/fsharp/ParseAndCheckInputs.fsi index 6fd5c897f82..4e8a9ace2ab 100644 --- a/src/fsharp/ParseAndCheckInputs.fsi +++ b/src/fsharp/ParseAndCheckInputs.fsi @@ -88,7 +88,7 @@ val TypeCheckOneInputEventually : NameResolution.TcResultsSink * TcState * ParsedInput * - quickCheck: bool + skipImplIfSigExists: bool -> Eventually<(TcEnv * TopAttribs * TypedImplFile option * ModuleOrNamespaceType) * TcState> /// Finish the checking of multiple inputs From a72a321c904508853a112748ddbb1544ce2a554c Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 2 Oct 2020 13:42:19 -0700 Subject: [PATCH 33/34] Minor update --- src/fsharp/service/service.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 80d52e78dd5..263dec809ea 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -255,7 +255,7 @@ type ScriptClosureCacheToken() = interface LockToken // There is only one instance of this type, held in FSharpChecker -type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enableLazyTypeChecking) as self = +type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking) as self = // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.reactor: The one and only Reactor let reactor = Reactor.Singleton let beforeFileChecked = Event() @@ -322,7 +322,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC options.UseScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, FSharpCheckerResultsSettings.maxTimeShareMilliseconds, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, enableBackgroundItemKeyStoreAndSemanticClassification, - enableLazyTypeChecking, + enablePartialTypeChecking, (if options.UseScriptResolutionRules then Some dependencyProviderForScripts else None)) match builderOpt with From 901712d15b203670119cd28a23423bed38f389a0 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 6 Oct 2020 13:20:05 -0700 Subject: [PATCH 34/34] Feedback --- src/fsharp/service/IncrementalBuild.fs | 8 +++++--- src/fsharp/service/IncrementalBuild.fsi | 2 +- src/fsharp/service/service.fs | 14 ++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 15902648e51..ad7ae10b31b 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1023,9 +1023,11 @@ module IncrementalBuilderEventTesting = module Tc = FSharp.Compiler.TypeChecker +// This module is only here to contain the SyntaxTree type as to avoid amiguity with the module FSharp.Compiler.SyntaxTree. [] module IncrementalBuildSyntaxTree = + /// Information needed to lazily parse a file to get a ParsedInput. Internally uses a weak cache. [] type SyntaxTree (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, filename: string, isLastCompiland) = @@ -1260,7 +1262,7 @@ type SemanticModel private (tcConfig: TcConfig, return state.Partial } - member this.TcInfoFull = + member this.TcInfoWithOptional = eventually { let! state = this.GetState(false) match state with @@ -1511,7 +1513,7 @@ type PartialCheckResults private (semanticModel: SemanticModel, timeStamp: DateT member _.TcInfo ctok = semanticModel.TcInfo |> eval ctok - member _.TcInfoFull ctok = semanticModel.TcInfoFull |> eval ctok + member _.TcInfoWithOptional ctok = semanticModel.TcInfoWithOptional |> eval ctok static member Create (semanticModel: SemanticModel, timestamp) = PartialCheckResults(semanticModel, timestamp) @@ -1757,7 +1759,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let tcInfo = semanticModel.TcInfo |> Eventually.force ctok tcInfo, None else - let tcInfo, tcInfoOptional = semanticModel.TcInfoFull |> Eventually.force ctok + let tcInfo, tcInfoOptional = semanticModel.TcInfoWithOptional |> Eventually.force ctok tcInfo, tcInfoOptional.latestImplFile tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, latestImplFile, tcInfo.latestCcuSigForFile) TypeCheckMultipleInputsFinish (results, finalInfo.tcState) diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 7537f1e3c81..db950e56473 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -107,7 +107,7 @@ type internal PartialCheckResults = /// Can cause a second type-check if `enablePartialTypeChecking` is true in the checker. /// Only use when it's absolutely necessary to get rich information on a file. - member TcInfoFull: CompilationThreadToken -> TcInfo * TcInfoOptional + member TcInfoWithOptional: CompilationThreadToken -> TcInfo * TcInfoOptional member TimeStamp: DateTime diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 263dec809ea..6a239d6bf83 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -709,7 +709,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) member __.GetBackgroundCheckResultsForFileInProject(filename, options, userOpName) = reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> - cancellable { + cancellable { let! builderOpt, creationErrors = getOrCreateBuilder (ctok, options, userOpName) match builderOpt with | None -> @@ -720,7 +720,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! (parseTreeOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let tcInfo, tcInfoOptional = tcProj.TcInfoFull ctok + let tcInfo, tcInfoOptional = tcProj.TcInfoWithOptional ctok let tcResolutionsRev = tcInfoOptional.tcResolutionsRev let tcSymbolUsesRev = tcInfoOptional.tcSymbolUsesRev @@ -730,9 +730,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile let latestImplementationFile = tcInfoOptional.latestImplFile let tcDependencyFiles = tcInfo.tcDependencyFiles - let tcErrors = tcInfo.TcErrors - let errorOptions = builder.TcConfig.errorSeverityOptions let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors, suggestNamesForErrors) |] let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcErrors, suggestNamesForErrors) |] @@ -763,7 +761,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC latestImplementationFile, List.head tcOpenDeclarationsRev) return (parseResults, typedResults) - } + } ) member __.FindReferencesInFile(filename: string, options: FSharpProjectOptions, symbol: FSharpSymbol, canInvalidateProject: bool, userOpName: string) = @@ -775,7 +773,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some builder -> if builder.ContainsFile filename then let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let _, tcInfoOptional = checkResults.TcInfoFull ctok + let _, tcInfoOptional = checkResults.TcInfoWithOptional ctok match tcInfoOptional.itemKeyStore with | None -> return Seq.empty | Some reader -> return reader.FindAll symbol.Item @@ -791,7 +789,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> return [||] | Some builder -> let! checkResults = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let _, tcInfoOptional = checkResults.TcInfoFull ctok + let _, tcInfoOptional = checkResults.TcInfoWithOptional ctok return tcInfoOptional.semanticClassification }) /// Try to get recent approximate type check results for a file. @@ -816,7 +814,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation - let tcInfo, tcInfoOptional = tcProj.TcInfoFull ctok + let tcInfo, tcInfoOptional = tcProj.TcInfoWithOptional ctok let tcSymbolUses = tcInfoOptional.TcSymbolUses let topAttribs = tcInfo.topAttribs