@@ -1428,8 +1428,9 @@ let CheckClosedInputSetFinish (declaredImpls: CheckedImplFile list, tcState) =
14281428 tcState, declaredImpls, ccuContents
14291429
14301430let CheckMultipleInputsSequential ( ctok , checkForErrors , tcConfig , tcImports , tcGlobals , prefixPathOpt , tcState , inputs ) =
1431+ let args = ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, false
14311432 ( tcState, inputs)
1432- ||> List.mapFold ( CheckOneInputEntry( ctok , checkForErrors , tcConfig , tcImports , tcGlobals , prefixPathOpt , false ) )
1433+ ||> List.mapFold ( CheckOneInputEntry args )
14331434
14341435/// Use parallel checking of implementation files that have signature files
14351436let CheckMultipleInputsInParallel
@@ -1542,12 +1543,190 @@ let CheckMultipleInputsInParallel
15421543 }
15431544
15441545 results, tcState)
1546+
1547+ type State = TcState * bool
1548+
1549+ /// Use parallel checking of implementation files that have signature files
1550+ let CheckMultipleInputsInParallel2
1551+ (
1552+ ctok ,
1553+ checkForErrors ,
1554+ tcConfig : TcConfig ,
1555+ tcImports : TcImports ,
1556+ tcGlobals ,
1557+ prefixPathOpt ,
1558+ tcState ,
1559+ eagerFormat ,
1560+ inputs
1561+ ) =
1562+
1563+ let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger
1564+
1565+ // We create one CapturingDiagnosticLogger for each file we are processing and
1566+ // ensure the diagnostics are presented in deterministic order.
1567+ //
1568+ // eagerFormat is used to format diagnostics as they are emitted, just as they would be in the command-line
1569+ // compiler. This is necessary because some formatting of diagnostics is dependent on the
1570+ // type inference state at precisely the time the diagnostic is emitted.
1571+ // TODO Does this mean we can't have more than one TcState with different contents?
1572+ // How does the formatting code know the state of type-checking/type inference? We don't pass it to the formatter directly
1573+ UseMultipleDiagnosticLoggers ( inputs, diagnosticsLogger, Some eagerFormat) ( fun inputsWithLoggers ->
1574+
1575+ // Equip loggers to locally filter w.r.t. scope pragmas in each input
1576+ let inputsWithLoggers : ( ParsedInput * DiagnosticsLogger )[] =
1577+ inputsWithLoggers
1578+ |> Seq.map ( fun ( input , oldLogger ) ->
1579+ let logger = DiagnosticsLoggerForInput( tcConfig, input, oldLogger)
1580+ input, logger)
1581+ |> Seq.toArray
1582+
1583+ // In the first linear part of parallel checking, we use a 'checkForErrors' that checks either for errors
1584+ // somewhere in the files processed prior to each one, or in the processing of this particular file.
1585+ let priorErrors = checkForErrors ()
1586+
1587+ // Grand experiment
1588+ // This code assumes the following file structure
1589+ // A.fs
1590+ // B1.fs (uses A)
1591+ // B2.fs (uses A, B1)
1592+ // C1.fs (uses A)
1593+ // C2.fs (uses A, C1)
1594+ // D.fs (uses A, B2, C2)
1595+ assert ( inputsWithLoggers.Length = 6 )
1596+
1597+ let fileDependencies =
1598+ [|
1599+ Set.empty
1600+ set [ 0 ]
1601+ set [ 0 ; 1 ]
1602+ set [ 0 ]
1603+ set [ 0 ; 3 ]
1604+ set [ 0 ; 2 ; 4 ]
1605+ |]
1606+
1607+ let partialResults , (( tcState , _ ) : State ) =
1608+ let lastIndex = inputsWithLoggers.Length - 1
1609+ let amap = tcImports.GetImportMap()
1610+
1611+ let conditionalDefines =
1612+ if tcConfig.noConditionalErasure then
1613+ None
1614+ else
1615+ Some tcConfig.conditionalDefines
1616+
1617+ // This function will type check all the files where it knows all the dependent file have already been seen.
1618+ // The `freeFiles` are a set of file indexes that have been type checked in a previous run.
1619+ // `processedFiles` stores the result of a typed checked file in a mutable fashion.
1620+ let rec visit (( currentTcState : TcState , currentPriorErrors : bool ) as state : State ) ( freeFiles : Set < int >) ( processedFiles : Choice < _ , _ > array ) =
1621+ // Find files that still needs processing.
1622+ let unprocessedFiles = freeFiles |> Set.difference ( set [| 0 .. lastIndex |])
1623+
1624+ if Set.isEmpty unprocessedFiles then
1625+ // All done
1626+ processedFiles, state
1627+ else
1628+ // What files can we type check from the files that are left to type check.
1629+ let nextFreeIndexes =
1630+ unprocessedFiles
1631+ |> Seq.choose ( fun fileIndex ->
1632+ let isFreeFile =
1633+ Seq.forall ( fun idx -> Set.contains idx freeFiles) fileDependencies.[ fileIndex]
1634+
1635+ if isFreeFile then
1636+ Some( fileIndex, inputsWithLoggers.[ fileIndex])
1637+ else
1638+ None)
1639+ |> Seq.toArray
1640+
1641+ // The next batch of files we can process in parallel
1642+ let next =
1643+ nextFreeIndexes
1644+ |> ArrayParallel.map ( fun ( fileIndex , ( input , logger )) ->
1645+ cancellable {
1646+ use _ = UseDiagnosticsLogger logger
1647+ // Is it OK that we don't update 'priorErrors' after processing batches?
1648+ let checkForErrors2 () = priorErrors || ( logger.ErrorCount > 0 )
1649+
1650+ // this is taken mostly from CheckOneInputAux, the case where the impl has no signature file
1651+ let file =
1652+ match input with
1653+ | ParsedInput.ImplFile file -> file
1654+ | ParsedInput.SigFile _ -> failwith " not expecting a signature file for now"
1655+
1656+ let tcSink = TcResultsSink.NoSink
1657+
1658+ // Typecheck the implementation file
1659+ let! topAttrs , implFile , tcEnvAtEnd , createsGeneratedProvidedTypes =
1660+ CheckOneImplFile(
1661+ tcGlobals,
1662+ amap,
1663+ currentTcState.tcsCcu,
1664+ currentTcState.tcsImplicitOpenDeclarations,
1665+ checkForErrors2,
1666+ conditionalDefines,
1667+ TcResultsSink.NoSink,
1668+ tcConfig.internalTestSpanStackReferring,
1669+ currentTcState.tcsTcImplEnv,
1670+ None,
1671+ file
1672+ )
1673+
1674+ return
1675+ ( fun tcState ->
1676+ let tcState =
1677+ { tcState with
1678+ tcsCreatesGeneratedProvidedTypes =
1679+ tcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes
1680+ }
1681+
1682+ let ccuSigForFile , updateTcState =
1683+ AddCheckResultsToTcState
1684+ ( tcGlobals,
1685+ amap,
1686+ false ,
1687+ prefixPathOpt,
1688+ tcSink,
1689+ tcState.tcsTcImplEnv,
1690+ input.QualifiedName,
1691+ implFile.Signature)
1692+ tcState
1693+
1694+ fileIndex, Choice1Of2( tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile), logger, updateTcState)
1695+ }
1696+ |> Cancellable.runWithoutCancellation)
1697+ |> fun results ->
1698+ (( currentTcState, currentPriorErrors), results)
1699+ ||> Array.fold ( fun ( tcState , priorErrors ) result ->
1700+ // the `result` callback ensure that the TcState is synced correctly after a batch of file has been type checked in parallel.
1701+ // I believe this bit cannot be done in parallel, yet the order in which we fold the state does not matter.
1702+ let fileIndex , partialResult , logger , nextTcState = result tcState
1703+
1704+ // Yikes!
1705+ processedFiles.[ fileIndex] <- partialResult
1706+
1707+ let priorErrors = priorErrors || ( logger.ErrorCount > 0 )
1708+ ( nextTcState, priorErrors))
1709+
1710+ // The next set of free files are the previous ones + the files we just type checked.
1711+ let nextFreeIndexes =
1712+ seq {
1713+ yield ! freeFiles
1714+ yield ! ( Seq.map fst nextFreeIndexes)
1715+ }
1716+ |> Set.ofSeq
1717+
1718+ // Next round!
1719+ visit next nextFreeIndexes processedFiles
1720+
1721+ visit ( tcState, priorErrors) Set.empty ( Array.zeroCreate inputsWithLoggers.Length)
1722+
1723+ partialResults, tcState)
15451724
15461725let CheckClosedInputSet ( ctok , checkForErrors , tcConfig : TcConfig , tcImports , tcGlobals , prefixPathOpt , tcState , eagerFormat , inputs ) =
15471726 // tcEnvAtEndOfLastFile is the environment required by fsi.exe when incrementally adding definitions
15481727 let results , tcState =
15491728 if tcConfig.parallelCheckingWithSignatureFiles then
1550- CheckMultipleInputsInParallel ( ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs)
1729+ CheckMultipleInputsInParallel2 ( ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs)
15511730 else
15521731 CheckMultipleInputsSequential( ctok, checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcState, inputs)
15531732
0 commit comments