Skip to content

Commit a7cb8c9

Browse files
committed
Type check backed implementation files in parallel in a second phase.
1 parent eb9b882 commit a7cb8c9

File tree

3 files changed

+146
-51
lines changed

3 files changed

+146
-51
lines changed

src/Compiler/Driver/ParseAndCheckInputs.fs

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,11 @@ let AddDummyCheckResultsToTcState
12151215

12161216
type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType
12171217

1218+
type PairResultOfImplementInPair =
1219+
Import.ImportMap * string list option * ModuleOrNamespaceType * bool * ParsedImplFileInput * TcState * ModuleOrNamespaceType
1220+
1221+
type PartialTypeCheckResult = Choice<PartialResult, PairResultOfImplementInPair>
1222+
12181223
type CheckArgs =
12191224
CompilationThreadToken * (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcState * (PhasedDiagnostic -> PhasedDiagnostic) * ParsedInput list
12201225

@@ -1406,7 +1411,7 @@ let CheckOneInputAux'
14061411
tcState: TcState,
14071412
inp: ParsedInput,
14081413
_skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool)
1409-
: Cancellable<TcState -> PartialResult * TcState> =
1414+
: Cancellable<TcState -> PartialTypeCheckResult * TcState> =
14101415

14111416
cancellable {
14121417
try
@@ -1468,7 +1473,7 @@ let CheckOneInputAux'
14681473
// Add the signature to the signature env (unless it had an explicit signature)
14691474
let ccuSigForFile = CombineCcuContentFragments [ sigFileType; tcState.tcsCcuSig ]
14701475

1471-
let partialResult = tcEnv, EmptyTopAttrs, None, ccuSigForFile
1476+
let partialResult = Choice1Of2(tcEnv, EmptyTopAttrs, None, ccuSigForFile)
14721477

14731478
let tcState =
14741479
{ tcState with
@@ -1489,55 +1494,76 @@ let CheckOneInputAux'
14891494
// Check if we've got an interface for this fragment
14901495
let rootSigOpt = tcState.tcsRootSigs.TryFind qualNameOfFile
14911496

1492-
// Typecheck the implementation file not backed by a signature file
1497+
match rootSigOpt with
1498+
| None ->
1499+
// Typecheck the implementation file not backed by a signature file
14931500

1494-
// Check if we've already seen an implementation for this fragment
1495-
if Zset.contains qualNameOfFile tcState.tcsRootImpls then
1496-
errorR (Error(FSComp.SR.buildImplementationAlreadyGiven (qualNameOfFile.Text), m))
1501+
// Check if we've already seen an implementation for this fragment
1502+
if Zset.contains qualNameOfFile tcState.tcsRootImpls then
1503+
errorR (Error(FSComp.SR.buildImplementationAlreadyGiven (qualNameOfFile.Text), m))
14971504

1498-
let! topAttrs, implFile, tcEnvAtEnd, createsGeneratedProvidedTypes =
1499-
CheckOneImplFile(
1500-
tcGlobals,
1501-
amap,
1502-
tcState.tcsCcu,
1503-
tcState.tcsImplicitOpenDeclarations,
1504-
checkForErrors,
1505-
conditionalDefines,
1506-
tcSink,
1507-
tcConfig.internalTestSpanStackReferring,
1508-
tcState.tcsTcImplEnv,
1509-
rootSigOpt,
1510-
file
1511-
)
1505+
let! topAttrs, implFile, tcEnvAtEnd, createsGeneratedProvidedTypes =
1506+
CheckOneImplFile(
1507+
tcGlobals,
1508+
amap,
1509+
tcState.tcsCcu,
1510+
tcState.tcsImplicitOpenDeclarations,
1511+
checkForErrors,
1512+
conditionalDefines,
1513+
tcSink,
1514+
tcConfig.internalTestSpanStackReferring,
1515+
tcState.tcsTcImplEnv,
1516+
rootSigOpt,
1517+
file
1518+
)
15121519

1513-
// printfn $"Finished Processing Impl {file.FileName}"
1514-
return
1515-
fun tcState ->
1516-
// let backed = rootSigOpt.IsSome
1517-
// printfn $"Applying Impl Backed={backed} {file.FileName}"
1520+
// printfn $"Finished Processing Impl {file.FileName}"
1521+
return
1522+
fun tcState ->
1523+
// let backed = rootSigOpt.IsSome
1524+
// printfn $"Applying Impl Backed={backed} {file.FileName}"
1525+
1526+
let ccuSigForFile, fsTcState =
1527+
AddCheckResultsToTcState
1528+
(tcGlobals, amap, false, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, implFile.Signature)
1529+
tcState
15181530

1519-
let ccuSigForFile, fsTcState =
1520-
AddCheckResultsToTcState
1521-
(tcGlobals, amap, false, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, implFile.Signature)
1522-
tcState
1531+
// backed impl files must not add results as there are already results from .fsi files
1532+
//let fsTcState = if backed then tcState else fsTcState
15231533

1524-
// backed impl files must not add results as there are already results from .fsi files
1525-
//let fsTcState = if backed then tcState else fsTcState
1534+
let partialResult = Choice1Of2(tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile)
15261535

1527-
let partialResult = tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile
1536+
let tcState =
1537+
{ fsTcState with
1538+
tcsCreatesGeneratedProvidedTypes =
1539+
fsTcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes
1540+
}
1541+
1542+
// printfn $"Finished applying Impl {file.FileName}"
1543+
partialResult, tcState
1544+
1545+
| Some rootSig ->
1546+
// Delay the typecheck the implementation file until the second phase of parallel processing.
1547+
// Adjust the TcState as if it has been checked, which makes the signature for the file available later
1548+
// in the compilation order.
1549+
let tcStateForImplFile = tcState
1550+
let qualNameOfFile = file.QualifiedName
1551+
let priorErrors = checkForErrors ()
15281552

1529-
let tcState =
1530-
{ fsTcState with
1531-
tcsCreatesGeneratedProvidedTypes =
1532-
fsTcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes
1533-
}
1553+
return
1554+
fun tcState ->
1555+
let ccuSigForFile, tcState =
1556+
AddCheckResultsToTcState
1557+
(tcGlobals, amap, true, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, rootSig)
1558+
tcState
15341559

1535-
// printfn $"Finished applying Impl {file.FileName}"
1536-
partialResult, tcState
1560+
let partialResult =
1561+
Choice2Of2(amap, conditionalDefines, rootSig, priorErrors, file, tcStateForImplFile, ccuSigForFile)
15371562

1563+
partialResult, tcState
15381564
with e ->
15391565
errorRecovery e range0
1540-
return fun tcState -> (tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState
1566+
return fun tcState -> Choice1Of2(tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState
15411567
}
15421568

15431569
/// Typecheck a single file (or interactive entry into F# Interactive). If skipImplIfSigExists is set to true
@@ -1552,7 +1578,7 @@ let CheckOneInput'
15521578
tcState: TcState,
15531579
input: ParsedInput,
15541580
skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool)
1555-
: Cancellable<TcState -> PartialResult * TcState> =
1581+
: Cancellable<TcState -> PartialTypeCheckResult * TcState> =
15561582
CheckOneInputAux'(checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input, skipImplIfSigExists)
15571583

15581584
// Within a file, equip loggers to locally filter w.r.t. scope pragmas in each input
@@ -1747,3 +1773,41 @@ let CheckClosedInputSet (ctok, checkForErrors, tcConfig: TcConfig, tcImports, tc
17471773

17481774
tcState.Ccu.Deref.Contents <- ccuContents
17491775
tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile
1776+
1777+
let checkBackedImplementationFile
1778+
(tcGlobals: TcGlobals)
1779+
(tcConfig: TcConfig)
1780+
(logger: DiagnosticsLogger)
1781+
(pairResult: PairResultOfImplementInPair)
1782+
=
1783+
let amap, conditionalDefines, rootSig, priorErrors, file, tcStateForImplFile, ccuSigForFile =
1784+
pairResult
1785+
1786+
// In the first linear part of parallel checking, we use a 'checkForErrors' that checks either for errors
1787+
// somewhere in the files processed prior to this one, including from the first phase, or in the processing
1788+
// of this particular file.
1789+
let checkForErrors2 () = priorErrors || (logger.ErrorCount > 0)
1790+
1791+
let topAttrs, implFile, tcEnvAtEnd, createsGeneratedProvidedTypes =
1792+
CheckOneImplFile(
1793+
tcGlobals,
1794+
amap,
1795+
tcStateForImplFile.tcsCcu,
1796+
tcStateForImplFile.tcsImplicitOpenDeclarations,
1797+
checkForErrors2,
1798+
conditionalDefines,
1799+
TcResultsSink.NoSink,
1800+
tcConfig.internalTestSpanStackReferring,
1801+
tcStateForImplFile.tcsTcImplEnv,
1802+
Some rootSig,
1803+
file
1804+
)
1805+
|> Cancellable.runWithoutCancellation
1806+
1807+
let result = (tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile)
1808+
result, createsGeneratedProvidedTypes
1809+
1810+
let updateCreatesGeneratedProvidedTypes tcState value =
1811+
{ tcState with
1812+
tcsCreatesGeneratedProvidedTypes = value
1813+
}

src/Compiler/Driver/ParseAndCheckInputs.fsi

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ val AddCheckResultsToTcState:
152152

153153
type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType
154154

155+
type PairResultOfImplementInPair =
156+
Import.ImportMap * string list option * ModuleOrNamespaceType * bool * ParsedImplFileInput * TcState * ModuleOrNamespaceType
157+
158+
type PartialTypeCheckResult = Choice<PartialResult, PairResultOfImplementInPair>
159+
160+
val checkBackedImplementationFile:
161+
TcGlobals -> TcConfig -> DiagnosticsLogger -> PairResultOfImplementInPair -> PartialResult * bool
162+
163+
val updateCreatesGeneratedProvidedTypes: TcState -> bool -> TcState
164+
155165
type CheckArgs =
156166
CompilationThreadToken * (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcState * (PhasedDiagnostic -> PhasedDiagnostic) * ParsedInput list
157167

@@ -185,7 +195,7 @@ val CheckOneInput':
185195
tcState: TcState *
186196
input: ParsedInput *
187197
skipImplIfSigExists: bool ->
188-
Cancellable<TcState -> PartialResult * TcState>
198+
Cancellable<TcState -> PartialTypeCheckResult * TcState>
189199

190200
val CheckMultipleInputsInParallel:
191201
(CompilationThreadToken * (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcState * (PhasedDiagnostic -> PhasedDiagnostic) * ParsedInput list) ->

tests/ParallelTypeCheckingTests/Code/ParallelTypeChecking.fs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ let DiagnosticsLoggerForInput (tcConfig: TcConfig, input: ParsedInput, oldLogger
3131

3232
type State = TcState * bool
3333
type FinalFileResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType
34-
type SingleResult = State -> FinalFileResult * State
34+
type SingleResult = State -> PartialTypeCheckResult * State
3535
type Item = File
3636

3737
type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType
3838

39-
let folder (state: State) (result: SingleResult) : FinalFileResult * State = result state
39+
let folder (state: State) (result: SingleResult) : PartialTypeCheckResult * State = result state
4040

4141
/// Use parallel checking of implementation files that have signature files
4242
let CheckMultipleInputsInParallel
@@ -54,7 +54,7 @@ let CheckMultipleInputsInParallel
5454
})
5555

5656
let graph = DependencyResolution.mkGraph sourceFiles
57-
graph |> Graph.map (fun idx -> sourceFiles.[idx].File) |> Graph.print
57+
// graph |> Graph.map (fun idx -> sourceFiles.[idx].File) |> Graph.print
5858

5959
let graphDumpPath =
6060
let graphDumpName =
@@ -80,7 +80,7 @@ let CheckMultipleInputsInParallel
8080
let processFile
8181
((input, logger): ParsedInput * DiagnosticsLogger)
8282
((currentTcState, _currentPriorErrors): State)
83-
: State -> PartialResult * State =
83+
: State -> PartialTypeCheckResult * State =
8484
cancellable {
8585
use _ = UseDiagnosticsLogger logger
8686
// printfn $"Processing AST {file.ToString()}"
@@ -111,7 +111,7 @@ let CheckMultipleInputsInParallel
111111
(fun (state: State) ->
112112
// printfn $"Applying {file.ToString()}"
113113
let tcState, priorErrors = state
114-
let (partialResult: PartialResult, tcState) = f tcState
114+
let (partialResult: PartialTypeCheckResult, tcState) = f tcState
115115

116116
let hasErrors = logger.ErrorCount > 0
117117
// TODO Should we use local _priorErrors or global priorErrors?
@@ -131,16 +131,16 @@ let CheckMultipleInputsInParallel
131131
let logger = DiagnosticsLoggerForInput(tcConfig, input, oldLogger)
132132
input, logger)
133133

134-
let processFile (fileIdx: int) (state: State) : State -> PartialResult * State =
134+
let processFile (fileIdx: int) (state: State) : State -> PartialTypeCheckResult * State =
135135
let parsedInput, logger = inputsWithLoggers.[fileIdx]
136136
processFile (parsedInput, logger) state
137137

138-
let folder: State -> SingleResult -> FinalFileResult * State = folder
138+
let folder: State -> SingleResult -> PartialTypeCheckResult * State = folder
139139
let _qnof = QualifiedNameOfFile.QualifiedNameOfFile(Ident("", Range.Zero))
140140
let state: State = tcState, priorErrors
141141

142142
let partialResults, (tcState, _) =
143-
GraphProcessing.processGraph<int, State, SingleResult, FinalFileResult>
143+
GraphProcessing.processGraph<int, State, SingleResult, PartialTypeCheckResult>
144144
graph
145145
processFile
146146
folder
@@ -150,4 +150,25 @@ let CheckMultipleInputsInParallel
150150
(fun _ -> true)
151151
10
152152

153-
partialResults |> Array.toList, tcState)
153+
// Do the parallel phase, checking all implementation files that did have a signature, in parallel.
154+
let results, createsGeneratedProvidedTypesFlags =
155+
Array.zip partialResults inputsWithLoggers
156+
|> ArrayParallel.map (fun (partialResult, (_, logger)) ->
157+
use _ = UseDiagnosticsLogger logger
158+
use _ = UseBuildPhase BuildPhase.TypeCheck
159+
160+
RequireCompilationThread ctok
161+
162+
match partialResult with
163+
| Choice1Of2 result -> result, false
164+
| Choice2Of2 backedImplResult -> checkBackedImplementationFile tcGlobals tcConfig logger backedImplResult)
165+
|> Array.toList
166+
|> List.unzip
167+
168+
let tcState =
169+
updateCreatesGeneratedProvidedTypes
170+
tcState
171+
(tcState.CreatesGeneratedProvidedTypes
172+
|| (createsGeneratedProvidedTypesFlags |> List.exists id))
173+
174+
results, tcState)

0 commit comments

Comments
 (0)