From 84041bb23dd4ccb0aaaf63746d7c70cf589d03c1 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 8 Dec 2020 18:48:47 -0800 Subject: [PATCH 1/2] Prevent impl files with backing sig files from invalidating the build --- src/fsharp/service/IncrementalBuild.fs | 54 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 64e1526dc3..dc5e8aefc7 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -143,6 +143,9 @@ module IncrementalBuildSyntaxTree = | _ -> parse sigNameOpt | _ -> parse sigNameOpt + member _.Invalidate() = + weakCache <- None + member _.FileName = filename /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. @@ -238,6 +241,23 @@ type SemanticModel private (tcConfig: TcConfig, member _.TcImports = tcImports + member _.BackingSignature = + match syntaxTreeOpt with + | Some syntaxTree -> + let sigFileName = Path.ChangeExtension(syntaxTree.FileName, ".fsi") + match prevTcInfo.sigNameOpt with + | Some (expectedSigFileName, sigName) when String.Equals(expectedSigFileName, sigFileName, StringComparison.OrdinalIgnoreCase) -> + Some sigName + | _ -> + None + | _ -> + None + + member _.Invalidate() = + lazyTcInfoState := None + syntaxTreeOpt + |> Option.iter (fun x -> x.Invalidate()) + member this.GetState(partialCheck: bool) = let partialCheck = // Only partial check if we have enabled it. @@ -344,7 +364,7 @@ type SemanticModel private (tcConfig: TcConfig, } } - member private _.TypeCheck (partialCheck: bool) = + member private this.TypeCheck (partialCheck: bool) = match partialCheck, !lazyTcInfoState with | true, Some (PartialState _ as state) | true, Some (FullState _ as state) -> state |> Eventually.Done @@ -357,12 +377,7 @@ type SemanticModel private (tcConfig: TcConfig, | Some syntaxTree -> let sigNameOpt = if partialCheck 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 + this.BackingSignature else None match syntaxTree.Parse sigNameOpt with @@ -910,7 +925,7 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let stampedFileNames = Array.init fileNames.Length (fun _ -> DateTime.MinValue) let stampedReferencedAssemblies = Array.init referencedAssemblies.Length (fun _ -> DateTime.MinValue) let mutable initialSemanticModel = None - let semanticModels = Array.zeroCreate fileNames.Length + let semanticModels = Array.zeroCreate fileNames.Length let mutable finalizedSemanticModel = None let computeStampedFileName (cache: TimeStampCache) (ctok: CompilationThreadToken) slot fileInfo cont = @@ -918,15 +933,20 @@ type IncrementalBuilder(tcGlobals, frameworkTcImports, nonFrameworkAssemblyInput let stamp = StampFileNameTask cache ctok fileInfo if currentStamp <> stamp then - // Something changed, the finalized view of the project must be invalidated. - finalizedSemanticModel <- None - - // Invalidate the file and all files below it. - stampedFileNames.[slot..] - |> Array.iteri (fun j _ -> - stampedFileNames.[slot + j] <- StampFileNameTask cache ctok fileNames.[slot + j] - semanticModels.[slot + j] <- None - ) + match semanticModels.[slot] with + // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. + | Some(semanticModel) when enablePartialTypeChecking && semanticModel.BackingSignature.IsSome -> + semanticModel.Invalidate() + | _ -> + // Something changed, the finalized view of the project must be invalidated. + finalizedSemanticModel <- None + + // Invalidate the file and all files below it. + stampedFileNames.[slot..] + |> Array.iteri (fun j _ -> + stampedFileNames.[slot + j] <- StampFileNameTask cache ctok fileNames.[slot + j] + semanticModels.[slot + j] <- None + ) if semanticModels.[slot].IsNone then cont slot fileInfo From b8e76c8696de3071f3fa6a7725845edee27d0f46 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 8 Dec 2020 19:00:05 -0800 Subject: [PATCH 2/2] Check states on invalidate --- src/fsharp/service/IncrementalBuild.fs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index dc5e8aefc7..297837968b 100755 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -253,8 +253,17 @@ type SemanticModel private (tcConfig: TcConfig, | _ -> None - member _.Invalidate() = - lazyTcInfoState := None + member this.Invalidate() = + let hasSig = this.BackingSignature.IsSome + match !lazyTcInfoState with + // If partial checking is enabled and we have a backing sig file, then do nothing. The partial state contains the sig state. + | Some(PartialState _) when enablePartialTypeChecking && hasSig -> () + // If partial checking is enabled and we have a backing sig file, then use the partial state. The partial state contains the sig state. + | Some(FullState(tcInfo, _)) when enablePartialTypeChecking && hasSig -> lazyTcInfoState := Some(PartialState tcInfo) + | _ -> + lazyTcInfoState := None + + // Always invalidate the syntax tree cache. syntaxTreeOpt |> Option.iter (fun x -> x.Invalidate())