Skip to content

Commit d848547

Browse files
authored
Multiple trie (dotnet#16098)
* Use Immutable structures for Trie types. Create incremental Trie instances.
1 parent 1c0ba9f commit d848547

File tree

9 files changed

+255
-225
lines changed

9 files changed

+255
-225
lines changed

src/Compiler/Driver/GraphChecking/DependencyResolution.fs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,29 @@ let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option =
1717

1818
visit trie path
1919

20-
let mapNodeToQueryResult (currentFileIndex: FileIndex) (node: TrieNode option) : QueryTrieNodeResult =
20+
let mapNodeToQueryResult (node: TrieNode option) : QueryTrieNodeResult =
2121
match node with
2222
| Some finalNode ->
23-
if
24-
Set.isEmpty finalNode.Files
25-
// If this node exposes files which the current index cannot see, we consider it not to have data at all.
26-
|| Set.forall (fun idx -> idx >= currentFileIndex) finalNode.Files
27-
then
23+
if Set.isEmpty finalNode.Files then
2824
QueryTrieNodeResult.NodeDoesNotExposeData
2925
else
3026
QueryTrieNodeResult.NodeExposesData(finalNode.Files)
3127
| None -> QueryTrieNodeResult.NodeDoesNotExist
3228

3329
/// <summary>Find a path in the Trie.</summary>
34-
let queryTrie (currentFileIndex: FileIndex) (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult =
35-
queryTriePartial trie path |> mapNodeToQueryResult currentFileIndex
30+
let queryTrie (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult =
31+
queryTriePartial trie path |> mapNodeToQueryResult
3632

3733
/// <summary>Same as 'queryTrie' but allows passing in a path combined from two parts, avoiding list allocation.</summary>
38-
let queryTrieDual (currentFileIndex: FileIndex) (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult =
34+
let queryTrieDual (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult =
3935
match queryTriePartial trie path1 with
4036
| Some intermediateNode -> queryTriePartial intermediateNode path2
4137
| None -> None
42-
|> mapNodeToQueryResult currentFileIndex
38+
|> mapNodeToQueryResult
4339

4440
/// Process namespace declaration.
4541
let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState =
46-
let queryResult = queryTrie state.CurrentFile trie path
42+
let queryResult = queryTrie trie path
4743

4844
match queryResult with
4945
| QueryTrieNodeResult.NodeDoesNotExist -> state
@@ -53,7 +49,7 @@ let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state:
5349
/// Process an "open" statement.
5450
/// The statement could link to files and/or should be tracked as an open namespace.
5551
let processOpenPath (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState =
56-
let queryResult = queryTrie state.CurrentFile trie path
52+
let queryResult = queryTrie trie path
5753

5854
match queryResult with
5955
| QueryTrieNodeResult.NodeDoesNotExist -> state
@@ -103,13 +99,12 @@ let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry
10399
||> Array.fold (fun state takeParts ->
104100
let path = List.take takeParts path
105101
// process the name was if it were a FQN
106-
let stateAfterFullIdentifier =
107-
processIdentifier (queryTrieDual state.CurrentFile trie [] path) state
102+
let stateAfterFullIdentifier = processIdentifier (queryTrieDual trie [] path) state
108103

109104
// Process the name in combination with the existing open namespaces
110105
(stateAfterFullIdentifier, state.OpenNamespaces)
111106
||> Set.fold (fun acc openNS ->
112-
let queryResult = queryTrieDual state.CurrentFile trie openNS path
107+
let queryResult = queryTrieDual trie openNS path
113108
processIdentifier queryResult acc))
114109

115110
| FileContentEntry.NestedModule (nestedContent = nestedContent) ->
@@ -142,7 +137,7 @@ let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (result: Fi
142137
// For each opened namespace, if none of already resolved dependencies define it, return the top-most file that defines it.
143138
Set.toArray result.OpenedNamespaces
144139
|> Array.choose (fun path ->
145-
match queryTrie fileIndex trie path with
140+
match queryTrie trie path with
146141
| QueryTrieNodeResult.NodeExposesData _
147142
| QueryTrieNodeResult.NodeDoesNotExist -> None
148143
| QueryTrieNodeResult.NodeDoesNotExposeData ->
@@ -201,20 +196,25 @@ let mkGraph (filePairs: FilePairMap) (files: FileInProject array) : Graph<FileIn
201196
else
202197
let fileContent = fileContents[file.Idx]
203198

204-
let knownFiles = [| 0 .. (file.Idx - 1) |] |> set
199+
// The Trie we want to use is the one that contains only files before our current index.
200+
// As we skip implementation files (backed by a signature), we cannot just use the current file index to find the right Trie.
201+
let trieForFile =
202+
trie
203+
|> Array.fold (fun acc (idx, t) -> if idx < file.Idx then t else acc) TrieNode.Empty
204+
205205
// File depends on all files above it that define accessible symbols at the root level (global namespace).
206-
let filesFromRoot = trie.Files |> Set.filter (fun rootIdx -> rootIdx < file.Idx)
206+
let filesFromRoot =
207+
trieForFile.Files |> Set.filter (fun rootIdx -> rootIdx < file.Idx)
207208
// Start by listing root-level dependencies.
208-
let initialDepsResult =
209-
(FileContentQueryState.Create file.Idx knownFiles filesFromRoot), fileContent
209+
let initialDepsResult = (FileContentQueryState.Create filesFromRoot), fileContent
210210
// Sequentially process all relevant entries of the file and keep updating the state and set of dependencies.
211211
let depsResult =
212212
initialDepsResult
213213
// Seq is faster than List in this case.
214-
||> Seq.fold (processStateEntry trie)
214+
||> Seq.fold (processStateEntry trieForFile)
215215

216216
// Add missing links for cases where an unused open namespace did not create a link.
217-
let ghostDependencies = collectGhostDependencies file.Idx trie depsResult
217+
let ghostDependencies = collectGhostDependencies file.Idx trieForFile depsResult
218218

219219
// Add a link from implementation files to their signature files.
220220
let signatureDependency =
@@ -237,4 +237,6 @@ let mkGraph (filePairs: FilePairMap) (files: FileInProject array) : Graph<FileIn
237237
|> Array.Parallel.map (fun file -> file.Idx, findDependencies file)
238238
|> readOnlyDict
239239

240+
let trie = trie |> Array.last |> snd
241+
240242
graph, trie

src/Compiler/Driver/GraphChecking/DependencyResolution.fsi

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ module internal FSharp.Compiler.GraphChecking.DependencyResolution
33

44
/// <summary>
55
/// Query a TrieNode to find a certain path.
6-
/// The result will take the current file index into account to determine if the result node contains data.
76
/// </summary>
87
/// <remarks>This code is only used directly in unit tests.</remarks>
9-
val queryTrie: currentFileIndex: FileIndex -> trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult
8+
val queryTrie: trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult
109

1110
/// <summary>Process an open path (found in the ParsedInput) with a given FileContentQueryState.</summary>
1211
/// <remarks>This code is only used directly in unit tests.</remarks>

0 commit comments

Comments
 (0)