diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index 33e5b6450d5..c3206efbdfb 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -17,29 +17,33 @@ let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option = visit trie path -let mapNodeToQueryResult (node: TrieNode option) : QueryTrieNodeResult = +let mapNodeToQueryResult (currentFileIndex: FileIndex) (node: TrieNode option) : QueryTrieNodeResult = match node with | Some finalNode -> - if Set.isEmpty finalNode.Files then + if + Set.isEmpty finalNode.Files + // If this node exposes files which the current index cannot see, we consider it not to have data at all. + || Set.forall (fun idx -> idx >= currentFileIndex) finalNode.Files + then QueryTrieNodeResult.NodeDoesNotExposeData else QueryTrieNodeResult.NodeExposesData(finalNode.Files) | None -> QueryTrieNodeResult.NodeDoesNotExist /// Find a path in the Trie. -let queryTrie (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult = - queryTriePartial trie path |> mapNodeToQueryResult +let queryTrie (currentFileIndex: FileIndex) (trie: TrieNode) (path: LongIdentifier) : QueryTrieNodeResult = + queryTriePartial trie path |> mapNodeToQueryResult currentFileIndex /// Same as 'queryTrie' but allows passing in a path combined from two parts, avoiding list allocation. -let queryTrieDual (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult = +let queryTrieDual (currentFileIndex: FileIndex) (trie: TrieNode) (path1: LongIdentifier) (path2: LongIdentifier) : QueryTrieNodeResult = match queryTriePartial trie path1 with | Some intermediateNode -> queryTriePartial intermediateNode path2 | None -> None - |> mapNodeToQueryResult + |> mapNodeToQueryResult currentFileIndex /// Process namespace declaration. let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = - let queryResult = queryTrie trie path + let queryResult = queryTrie state.CurrentFile trie path match queryResult with | QueryTrieNodeResult.NodeDoesNotExist -> state @@ -49,7 +53,7 @@ let processNamespaceDeclaration (trie: TrieNode) (path: LongIdentifier) (state: /// Process an "open" statement. /// The statement could link to files and/or should be tracked as an open namespace. let processOpenPath (trie: TrieNode) (path: LongIdentifier) (state: FileContentQueryState) : FileContentQueryState = - let queryResult = queryTrie trie path + let queryResult = queryTrie state.CurrentFile trie path match queryResult with | QueryTrieNodeResult.NodeDoesNotExist -> state @@ -99,12 +103,13 @@ let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry ||> Array.fold (fun state takeParts -> let path = List.take takeParts path // process the name was if it were a FQN - let stateAfterFullIdentifier = processIdentifier (queryTrieDual trie [] path) state + let stateAfterFullIdentifier = + processIdentifier (queryTrieDual state.CurrentFile trie [] path) state // Process the name in combination with the existing open namespaces (stateAfterFullIdentifier, state.OpenNamespaces) ||> Set.fold (fun acc openNS -> - let queryResult = queryTrieDual trie openNS path + let queryResult = queryTrieDual state.CurrentFile trie openNS path processIdentifier queryResult acc)) | FileContentEntry.NestedModule (nestedContent = nestedContent) -> @@ -137,7 +142,7 @@ let collectGhostDependencies (fileIndex: FileIndex) (trie: TrieNode) (result: Fi // For each opened namespace, if none of already resolved dependencies define it, return the top-most file that defines it. Set.toArray result.OpenedNamespaces |> Array.choose (fun path -> - match queryTrie trie path with + match queryTrie fileIndex trie path with | QueryTrieNodeResult.NodeExposesData _ | QueryTrieNodeResult.NodeDoesNotExist -> None | QueryTrieNodeResult.NodeDoesNotExposeData -> diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi index 3ceac318ec7..a2c52adcea9 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fsi @@ -1,9 +1,12 @@ /// Logic for constructing a file dependency graph for the purposes of parallel type-checking. module internal FSharp.Compiler.GraphChecking.DependencyResolution -/// Query a TrieNode to find a certain path. +/// +/// Query a TrieNode to find a certain path. +/// The result will take the current file index into account to determine if the result node contains data. +/// /// This code is only used directly in unit tests. -val queryTrie: trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult +val queryTrie: currentFileIndex: FileIndex -> trie: TrieNode -> path: LongIdentifier -> QueryTrieNodeResult /// Process an open path (found in the ParsedInput) with a given FileContentQueryState. /// This code is only used directly in unit tests. diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs index 1c06fe9dee2..b82014b178a 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/QueryTrieTests.fs @@ -762,7 +762,7 @@ let private fantomasCoreTrie: TrieNode = [] let ``Query non existing node in trie`` () = let result = - queryTrie fantomasCoreTrie [ "System"; "System"; "Runtime"; "CompilerServices" ] + queryTrie 7 fantomasCoreTrie [ "System"; "System"; "Runtime"; "CompilerServices" ] match result with | QueryTrieNodeResult.NodeDoesNotExist -> Assert.Pass() @@ -770,7 +770,7 @@ let ``Query non existing node in trie`` () = [] let ``Query node that does not expose data in trie`` () = - let result = queryTrie fantomasCoreTrie [ "Fantomas"; "Core" ] + let result = queryTrie 7 fantomasCoreTrie [ "Fantomas"; "Core" ] match result with | QueryTrieNodeResult.NodeDoesNotExposeData -> Assert.Pass() @@ -779,7 +779,7 @@ let ``Query node that does not expose data in trie`` () = [] let ``Query module node that exposes one file`` () = let result = - queryTrie fantomasCoreTrie [ "Fantomas"; "Core"; "ISourceTextExtensions" ] + queryTrie 7 fantomasCoreTrie [ "Fantomas"; "Core"; "ISourceTextExtensions" ] match result with | QueryTrieNodeResult.NodeExposesData file -> diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs index a60e8e7c116..1211e6b19f3 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs @@ -747,6 +747,28 @@ module S let main _ = F.br () 0 +""" + (set [| 0 |]) + ] + scenario + "Ghost dependency via top-level namespace" + [ + sourceFile + "Graph.fs" + """ +namespace Graphoscope.Graph + +type UndirectedGraph = obj +""" + Set.empty + sourceFile + "DiGraph.fs" + """ +namespace Graphoscope + +open Graphoscope + +type DiGraph = obj """ (set [| 0 |]) ]