Skip to content

Commit 73a100f

Browse files
committed
changes
1 parent 89c44e0 commit 73a100f

13 files changed

Lines changed: 452 additions & 5 deletions

File tree

FSharp.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
109109
EndProject
110110
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.Tests2", "tests\FSharp.Compiler.Service.Tests2\FSharp.Compiler.Service.Tests2.fsproj", "{7F58653C-1B72-41C7-9A06-43CAAC3051B5}"
111111
EndProject
112+
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "DiamondTest", "tests\DiamondTest\DiamondTest.fsproj", "{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}"
113+
EndProject
112114
Global
113115
GlobalSection(SolutionConfigurationPlatforms) = preSolution
114116
Debug|Any CPU = Debug|Any CPU
@@ -443,6 +445,18 @@ Global
443445
{7F58653C-1B72-41C7-9A06-43CAAC3051B5}.Release|Any CPU.Build.0 = Release|Any CPU
444446
{7F58653C-1B72-41C7-9A06-43CAAC3051B5}.Release|x86.ActiveCfg = Release|Any CPU
445447
{7F58653C-1B72-41C7-9A06-43CAAC3051B5}.Release|x86.Build.0 = Release|Any CPU
448+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
449+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
450+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Debug|x86.ActiveCfg = Debug|Any CPU
451+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Debug|x86.Build.0 = Debug|Any CPU
452+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
453+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Proto|Any CPU.Build.0 = Debug|Any CPU
454+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Proto|x86.ActiveCfg = Debug|Any CPU
455+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Proto|x86.Build.0 = Debug|Any CPU
456+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
457+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Release|Any CPU.Build.0 = Release|Any CPU
458+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Release|x86.ActiveCfg = Release|Any CPU
459+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC}.Release|x86.Build.0 = Release|Any CPU
446460
EndGlobalSection
447461
GlobalSection(SolutionProperties) = preSolution
448462
HideSolutionNode = FALSE
@@ -476,6 +490,7 @@ Global
476490
{BEC6E796-7E53-4888-AAFC-B8FD55C425DF} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
477491
{9C7523BA-7AB2-4604-A5FD-653E82C2BAD1} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
478492
{7F58653C-1B72-41C7-9A06-43CAAC3051B5} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
493+
{B7C957CB-9E64-44CF-BC73-152BFC6E5BCC} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
479494
EndGlobalSection
480495
GlobalSection(ExtensibilityGlobals) = postSolution
481496
SolutionGuid = {BD5177C7-1380-40E7-94D2-7768E1A8B1B8}

src/Compiler/Driver/ParseAndCheckInputs.fs

Lines changed: 181 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,8 +1428,9 @@ let CheckClosedInputSetFinish (declaredImpls: CheckedImplFile list, tcState) =
14281428
tcState, declaredImpls, ccuContents
14291429

14301430
let 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
14351436
let 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

15461725
let 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

tests/DiamondTest/A.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.A
2+
3+
let a = 1

tests/DiamondTest/B1.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.B1
2+
3+
let b1 = A.a + 10

tests/DiamondTest/B2.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.B2
2+
3+
let b2 = B1.b1 + 100

tests/DiamondTest/C1.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.C1
2+
3+
let c1 = A.a + 30

tests/DiamondTest/C2.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.C2
2+
3+
let c2 = C1.c1 + 300

tests/DiamondTest/D.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module DiamondTest.D
2+
3+
let d = C2.c2 + B2.b2
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net7.0</TargetFramework>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<Compile Include="A.fs" />
10+
<Compile Include="B1.fs" />
11+
<Compile Include="B2.fs" />
12+
<Compile Include="C1.fs" />
13+
<Compile Include="C2.fs" />
14+
<Compile Include="D.fs" />
15+
</ItemGroup>
16+
17+
</Project>

tests/FSharp.Compiler.Service.Tests2/FSharp.Compiler.Service.Tests2.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
<Compile Include="Tests\TestDepResolving.fs" />
4444
<Compile Include="Tests\RunCompiler.fs" />
4545
<Compile Include="Program.fs" />
46+
<Content Include="SimpleArgs.txt" />
4647
</ItemGroup>
4748

4849
<ItemGroup>

0 commit comments

Comments
 (0)