Hello,
I've done some digging into the benefits of having a signature file in your editor.
The FSharpChecker has a setting FSharpChecker.Create(enablePartialTypeChecking = true).
Rider enables this via:
When you invoke checker.ParseAndCheckFileInProject, the backgroundChecker will construct a BoundModel inside an IncrementalBuilder :
service.fs:
bc.ParseAndCheckFileInProject ->
getOrCreateBuilder ->
createBuilderNode ->
CreateOneIncrementalBuilder ->
IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions
IncrementalBuilder.fs:
TryCreateIncrementalBuilderForProjectOptions (takes enablePartialTypeChecking:bool) ->
IncrementalBuilderState.Create(initialState)
This IncrementalBuilderState.Create will create a BoundModel for each fsharp file.
https://github.com/dotnet/fsharp/blob/f4f0e7e5173f525b4c3ff6476d82f60c9f01fbf4/src/Compiler/Service/IncrementalBuild.fs#L1146-L1147
Using IncrementalBuilderStateHelpers.createBoundModelGraphNode, which will construct a GraphNode that calls IncrementalBuilderHelpers.TypeCheckTask.
This TypeCheckTask will call prevBoundModel.Next for the current file the evalutation of the GraphNode will call BoundModel.TypeCheck.
In TypeCheckTask, we also see prevBoundModel.Next which goes to the next file.
BoundModel calls private TypeCheck in the constructor, we see our first glimps of enablePartialTypeChecking (named partialCheck).
let sigNameOpt =
if partialCheck then
this.BackingSignature
else
NoneIf you have a signature file: syntaxTree.Parse will return:
let canSkip = sigNameOpt.IsSome && FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName)
let input =
if canSkip then
ParsedInput.ImplFile(
ParsedImplFileInput(
fileName,
false,
sigNameOpt.Value,
[],
[],
[],
isLastCompiland,
{ ConditionalDirectives = []; CodeComments = [] }
)
)Notice the rather empty syntax tree.
In CheckOneInput (CheckOneInputAux actually),
// Check if we've got an interface for this fragment
let rootSigOpt = tcState.tcsRootSigs.TryFind qualNameOfFileThey will try and find the signature type infomation based on the qualNameOfFile.
If that is found (and the setting is active), the TypeChecking will be skipped entirely:
match rootSigOpt with
| Some rootSig when skipImplIfSigExists ->
// Delay the typecheck the implementation file until the second phase of parallel processing.
// Adjust the TcState as if it has been checked, which makes the signature for the file available later
// in the compilation order.
let tcStateForImplFile = tcState
let qualNameOfFile = file.QualifiedName
let priorErrors = checkForErrors ()
let ccuSigForFile, tcState =
AddCheckResultsToTcState
(tcGlobals, amap, hadSig, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, rootSig)
tcState
let partialResult =
(amap, conditionalDefines, rootSig, priorErrors, file, tcStateForImplFile, ccuSigForFile)
return Choice2Of2 partialResult, tcStateCheckOneInputAux is also being called from CheckMultipleInputsInParallel in the parallal PR:
And that somewhat explains the benefit of signature files both in the FSharpChecker and during compilation.
Did you have enablePartialTypeChecking = true in the benchmark test?
