11module internal FSharp.Compiler.GraphChecking.DependencyResolution
22
3- open FSharp.Compiler .IO
43open FSharp.Compiler .Syntax
4+ open Internal.Utilities .Library
55
6- /// <summary>Find a path in the Trie.</summary>
7- /// <remarks>This function could be cached in future if performance is an issue.</remarks>
8- let queryTrie ( trie : TrieNode ) ( path : LongIdentifier ) : QueryTrieNodeResult =
6+ /// <summary>Find a path from a starting TrieNode and return the end node or None</summary>
7+ let queryTriePartial ( trie : TrieNode ) ( path : LongIdentifier ) : TrieNode option =
98 let rec visit ( currentNode : TrieNode ) ( path : LongIdentifier ) =
109 match path with
11- | [] -> failwith " path should not be empty"
12- | [ lastNodeFromPath ] ->
13- match currentNode.Children.TryGetValue( lastNodeFromPath) with
14- | false , _ -> QueryTrieNodeResult.NodeDoesNotExist
15- | true , childNode ->
16- if Set.isEmpty childNode.Files then
17- QueryTrieNodeResult.NodeDoesNotExposeData
18- else
19- QueryTrieNodeResult.NodeExposesData( childNode.Files)
10+ // When we get through all partial identifiers, we've reached the node the full path points to.
11+ | [] -> Some currentNode
12+ // More segments to get through
2013 | currentPath :: restPath ->
2114 match currentNode.Children.TryGetValue( currentPath) with
22- | false , _ -> QueryTrieNodeResult.NodeDoesNotExist
15+ | false , _ -> None
2316 | true , childNode -> visit childNode restPath
2417
2518 visit trie path
2619
27- let queryTrieMemoized ( trie : TrieNode ) : QueryTrie =
28- Internal.Utilities.Library.Tables.memoize ( queryTrie trie)
20+ let mapNodeToQueryResult ( node : TrieNode option ) : QueryTrieNodeResult =
21+ match node with
22+ | Some finalNode ->
23+ if Set.isEmpty finalNode.Files then
24+ QueryTrieNodeResult.NodeDoesNotExposeData
25+ else
26+ QueryTrieNodeResult.NodeExposesData( finalNode.Files)
27+ | None -> QueryTrieNodeResult.NodeDoesNotExist
28+
29+ /// <summary>Find a path in the Trie.</summary>
30+ let queryTrie ( trie : TrieNode ) ( path : LongIdentifier ) : QueryTrieNodeResult =
31+ queryTriePartial trie path |> mapNodeToQueryResult
32+
33+ /// <summary>Same as 'queryTrie' but allows passing in a path combined from two parts, avoiding list allocation.</summary>
34+ let queryTrieDual ( trie : TrieNode ) ( path1 : LongIdentifier ) ( path2 : LongIdentifier ) : QueryTrieNodeResult =
35+ match queryTriePartial trie path1 with
36+ | Some intermediateNode -> queryTriePartial intermediateNode path2
37+ | None -> None
38+ |> mapNodeToQueryResult
2939
3040/// Process namespace declaration.
31- let processNamespaceDeclaration ( queryTrie : QueryTrie ) ( path : LongIdentifier ) ( state : FileContentQueryState ) : FileContentQueryState =
32- let queryResult = queryTrie path
41+ let processNamespaceDeclaration ( trie : TrieNode ) ( path : LongIdentifier ) ( state : FileContentQueryState ) : FileContentQueryState =
42+ let queryResult = queryTrie trie path
3343
3444 match queryResult with
3545 | QueryTrieNodeResult.NodeDoesNotExist -> state
@@ -38,18 +48,16 @@ let processNamespaceDeclaration (queryTrie: QueryTrie) (path: LongIdentifier) (s
3848
3949/// Process an "open" statement.
4050/// The statement could link to files and/or should be tracked as an open namespace.
41- let processOpenPath ( queryTrie : QueryTrie ) ( path : LongIdentifier ) ( state : FileContentQueryState ) : FileContentQueryState =
42- let queryResult = queryTrie path
51+ let processOpenPath ( trie : TrieNode ) ( path : LongIdentifier ) ( state : FileContentQueryState ) : FileContentQueryState =
52+ let queryResult = queryTrie trie path
4353
4454 match queryResult with
4555 | QueryTrieNodeResult.NodeDoesNotExist -> state
4656 | QueryTrieNodeResult.NodeDoesNotExposeData -> state.AddOpenNamespace path
4757 | QueryTrieNodeResult.NodeExposesData files -> state.AddOpenNamespace( path, files)
4858
4959/// Process an identifier.
50- let processIdentifier ( queryTrie : QueryTrie ) ( path : LongIdentifier ) ( state : FileContentQueryState ) : FileContentQueryState =
51- let queryResult = queryTrie path
52-
60+ let processIdentifier ( queryResult : QueryTrieNodeResult ) ( state : FileContentQueryState ) : FileContentQueryState =
5361 match queryResult with
5462 | QueryTrieNodeResult.NodeDoesNotExist -> state
5563 | QueryTrieNodeResult.NodeDoesNotExposeData ->
@@ -59,26 +67,26 @@ let processIdentifier (queryTrie: QueryTrie) (path: LongIdentifier) (state: File
5967 | QueryTrieNodeResult.NodeExposesData files -> state.AddDependencies files
6068
6169/// Typically used to fold FileContentEntry items over a FileContentQueryState
62- let rec processStateEntry ( queryTrie : QueryTrie ) ( state : FileContentQueryState ) ( entry : FileContentEntry ) : FileContentQueryState =
70+ let rec processStateEntry ( trie : TrieNode ) ( state : FileContentQueryState ) ( entry : FileContentEntry ) : FileContentQueryState =
6371 match entry with
6472 | FileContentEntry.TopLevelNamespace ( topLevelPath, content) ->
6573 let state =
6674 match topLevelPath with
6775 | [] -> state
68- | _ -> processNamespaceDeclaration queryTrie topLevelPath state
76+ | _ -> processNamespaceDeclaration trie topLevelPath state
6977
70- List.fold ( processStateEntry queryTrie ) state content
78+ List.fold ( processStateEntry trie ) state content
7179
7280 | FileContentEntry.OpenStatement path ->
7381 // An open statement can directly reference file or be a partial open statement
7482 // Both cases need to be processed.
75- let stateAfterFullOpenPath = processOpenPath queryTrie path state
83+ let stateAfterFullOpenPath = processOpenPath trie path state
7684
77- // Any existing open statement could be extended with the current path (if that node where to exists in the trie)
85+ // Any existing open statement could be extended with the current path (if that node were to exists in the trie)
7886 // The extended path could add a new link (in case of a module or namespace with types)
79- // It might also not add anything at all (in case it the extended path is still a partial one)
87+ // It might also not add anything at all (in case the extended path is still a partial one)
8088 ( stateAfterFullOpenPath, state.OpenNamespaces)
81- ||> Set.fold ( fun acc openNS -> processOpenPath queryTrie [ yield ! openNS; yield ! path ] acc)
89+ ||> Set.fold ( fun acc openNS -> processOpenPath trie [ yield ! openNS; yield ! path ] acc)
8290
8391 | FileContentEntry.PrefixedIdentifier path ->
8492 match path with
@@ -91,15 +99,17 @@ let rec processStateEntry (queryTrie: QueryTrie) (state: FileContentQueryState)
9199 ||> Array.fold ( fun state takeParts ->
92100 let path = List.take takeParts path
93101 // process the name was if it were a FQN
94- let stateAfterFullIdentifier = processIdentifier queryTrie path state
102+ let stateAfterFullIdentifier = processIdentifier ( queryTrieDual trie [] path) state
95103
96104 // Process the name in combination with the existing open namespaces
97105 ( stateAfterFullIdentifier, state.OpenNamespaces)
98- ||> Set.fold ( fun acc openNS -> processIdentifier queryTrie [ yield ! openNS; yield ! path ] acc))
106+ ||> Set.fold ( fun acc openNS ->
107+ let queryResult = queryTrieDual trie openNS path
108+ processIdentifier queryResult acc))
99109
100110 | FileContentEntry.NestedModule ( nestedContent = nestedContent) ->
101111 // We don't want our current state to be affect by any open statements in the nested module
102- let nestedState = List.fold ( processStateEntry queryTrie ) state nestedContent
112+ let nestedState = List.fold ( processStateEntry trie ) state nestedContent
103113 // Afterward we are only interested in the found dependencies in the nested module
104114 let foundDependencies =
105115 Set.union state.FoundDependencies nestedState.FoundDependencies
@@ -123,11 +133,11 @@ let rec processStateEntry (queryTrie: QueryTrie) (state: FileContentQueryState)
123133/// This function returns an array with a potential extra dependencies that makes sure that any such namespaces can be resolved (if they exists).
124134/// For each unused namespace `open` we return at most one file that defines that namespace.</para>
125135/// </remarks>
126- let collectGhostDependencies ( fileIndex : FileIndex ) ( trie : TrieNode ) ( queryTrie : QueryTrie ) ( result : FileContentQueryState ) =
136+ let collectGhostDependencies ( fileIndex : FileIndex ) ( trie : TrieNode ) ( result : FileContentQueryState ) =
127137 // For each opened namespace, if none of already resolved dependencies define it, return the top-most file that defines it.
128138 Set.toArray result.OpenedNamespaces
129139 |> Array.choose ( fun path ->
130- match queryTrie path with
140+ match queryTrie trie path with
131141 | QueryTrieNodeResult.NodeExposesData _
132142 | QueryTrieNodeResult.NodeDoesNotExist -> None
133143 | QueryTrieNodeResult.NodeDoesNotExposeData ->
@@ -170,7 +180,6 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP
170180 | ParsedInput.SigFile _ -> Some f)
171181
172182 let trie = TrieMapping.mkTrie trieInput
173- let queryTrie : QueryTrie = queryTrieMemoized trie
174183
175184 let fileContents =
176185 files
@@ -180,14 +189,31 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP
180189 else
181190 FileContentMapping.mkFileContent file)
182191
192+ // Files in FSharp.Core have an implicit dependency on `prim-types-prelude.fsi` - add it.
193+ let fsharpCoreImplicitDependency =
194+ if compilingFSharpCore then
195+ let filename = " prim-types-prelude.fsi"
196+
197+ let implicitDepIdx =
198+ files
199+ |> Array.tryFindIndex ( fun f -> System.IO.Path.GetFileName( f.FileName) = filename)
200+
201+ match implicitDepIdx with
202+ | Some idx -> Some idx
203+ | None ->
204+ exn $" Expected to find file '{filename}' during compilation of FSharp.Core, but it was not found."
205+ |> raise
206+ else
207+ None
208+
183209 let findDependencies ( file : FileInProject ) : FileIndex array =
184210 if file.Idx = 0 then
185211 // First file cannot have any dependencies.
186212 Array.empty
187213 else
188214 let fileContent = fileContents[ file.Idx]
189215
190- let knownFiles = [ 0 .. ( file.Idx - 1 ) ] |> set
216+ let knownFiles = [| 0 .. ( file.Idx - 1 ) | ] |> set
191217 // File depends on all files above it that define accessible symbols at the root level (global namespace).
192218 let filesFromRoot = trie.Files |> Set.filter ( fun rootIdx -> rootIdx < file.Idx)
193219 // Start by listing root-level dependencies.
@@ -197,42 +223,28 @@ let mkGraph (compilingFSharpCore: bool) (filePairs: FilePairMap) (files: FileInP
197223 let depsResult =
198224 initialDepsResult
199225 // Seq is faster than List in this case.
200- ||> Seq.fold ( processStateEntry queryTrie )
226+ ||> Seq.fold ( processStateEntry trie )
201227
202228 // Add missing links for cases where an unused open namespace did not create a link.
203- let ghostDependencies = collectGhostDependencies file.Idx trie queryTrie depsResult
229+ let ghostDependencies = collectGhostDependencies file.Idx trie depsResult
204230
205231 // Add a link from implementation files to their signature files.
206232 let signatureDependency =
207233 match filePairs.TryGetSignatureIndex file.Idx with
208234 | None -> Array.empty
209235 | Some sigIdx -> Array.singleton sigIdx
210236
211- // Files in FSharp.Core have an implicit dependency on `prim-types-prelude.fsi` - add it.
212- let fsharpCoreImplicitDependencies =
213- let filename = " prim-types-prelude.fsi"
214-
215- let implicitDepIdx =
216- files
217- |> Array.tryFindIndex ( fun f -> FileSystemUtils.fileNameOfPath f.FileName = filename)
218-
219- [|
220- if compilingFSharpCore then
221- match implicitDepIdx with
222- | Some idx ->
223- if file.Idx > idx then
224- yield idx
225- | None ->
226- exn $" Expected to find file '{filename}' during compilation of FSharp.Core, but it was not found."
227- |> raise
228- |]
237+ let fsharpCoreImplicitDependencyForThisFile =
238+ match fsharpCoreImplicitDependency with
239+ | Some depIdx when file.Idx > depIdx -> [| depIdx |]
240+ | _ -> [||]
229241
230242 let allDependencies =
231243 [|
232244 yield ! depsResult.FoundDependencies
233245 yield ! ghostDependencies
234246 yield ! signatureDependency
235- yield ! fsharpCoreImplicitDependencies
247+ yield ! fsharpCoreImplicitDependencyForThisFile
236248 |]
237249 |> Array.distinct
238250
0 commit comments