Skip to content

Commit 9d3b220

Browse files
author
David Ungar
authored
Merge pull request #740 from davidungar/july-cull
[Incremental] When reading priors, don't use nodes for removed inputs.
2 parents c733048 + c871c37 commit 9d3b220

File tree

4 files changed

+49
-36
lines changed

4 files changed

+49
-36
lines changed

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,22 @@ extension ModuleDependencyGraph {
129129
invalidatedNodes.formUnion(self.integrateExternal(.known(fed)))
130130
}
131131
}
132+
133+
/// Determine whether (deserialized) node was for a definition in a source file that is no longer part of the build.
134+
///
135+
/// If the priors were read from an invocation containing a subsuequently removed input,
136+
/// the nodes defining decls from that input must be culled.
137+
///
138+
/// - Parameter node: The (deserialized) node to test.
139+
/// - Returns: true iff the node corresponds to a definition on a removed source file.
140+
fileprivate func isForRemovedInput(_ node: Node) -> Bool {
141+
guard let fileWithDeps = node.dependencySource?.typedFile,
142+
fileWithDeps.type == .swift // e.g., could be a .swiftdeps file
143+
else {
144+
return false
145+
}
146+
return !isPartOfBuild(fileWithDeps)
147+
}
132148
}
133149

134150
// MARK: - Scheduling the first wave
@@ -532,7 +548,13 @@ extension ModuleDependencyGraph {
532548
private var identifiers: [String] = [""]
533549
private var currentDefKey: DependencyKey? = nil
534550
private var nodeUses: [(DependencyKey, Int)] = []
535-
public private(set) var allNodes: [Node] = []
551+
552+
/// Deserialized nodes, in order appearing in the priors file. If `nil`, the node is for a removed source file.
553+
///
554+
/// Since the def-use relationship is serialized according the index of the node in the priors file, this
555+
/// `Array` supports the deserialization of the def-use links by mapping index to node.
556+
/// The optionality of the contents lets the ``ModuleDependencyGraph/isForRemovedInput`` check to be cached.
557+
public private(set) var potentiallyUsedNodes: [Node?] = []
536558

537559
init?(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
538560
self.fileSystem = info.fileSystem
@@ -545,8 +567,12 @@ extension ModuleDependencyGraph {
545567

546568
func finalizeGraph() -> ModuleDependencyGraph {
547569
for (dependencyKey, useID) in self.nodeUses {
570+
guard let use = self.potentiallyUsedNodes[useID] else {
571+
// Don't record uses of defs of removed files.
572+
continue
573+
}
548574
let isNewUse = self.graph.nodeFinder
549-
.record(def: dependencyKey, use: self.allNodes[useID])
575+
.record(def: dependencyKey, use: use)
550576
assert(isNewUse, "Duplicate use def-use arc in graph?")
551577
}
552578
return self.graph
@@ -565,7 +591,12 @@ extension ModuleDependencyGraph {
565591
mutating func didExitBlock() throws {}
566592

567593
private mutating func finalize(node newNode: Node) {
568-
self.allNodes.append(newNode)
594+
if graph.isForRemovedInput(newNode) {
595+
// Preserve the mapping of Int to Node for reconstructing def-use links with a placeholder.
596+
self.potentiallyUsedNodes.append(nil)
597+
return
598+
}
599+
self.potentiallyUsedNodes.append(newNode)
569600
let oldNode = self.graph.nodeFinder.insert(newNode)
570601
assert(oldNode == nil,
571602
"Integrated the same node twice: \(oldNode!), \(newNode)")

Tests/SwiftDriverTests/DependencyGraphSerializationTests.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import TSCBasic
1616
import TSCUtility
1717

1818
class DependencyGraphSerializationTests: XCTestCase, ModuleDependencyGraphMocker {
19-
static let mockGraphCreator = MockModuleDependencyGraphCreator(maxIndex: 12)
19+
static let maxIndex = 12
20+
static let mockGraphCreator = MockModuleDependencyGraphCreator(maxIndex: maxIndex)
2021

2122
/// Unit test of the `ModuleDependencyGraph` serialization
2223
///
@@ -33,8 +34,9 @@ class DependencyGraphSerializationTests: XCTestCase, ModuleDependencyGraphMocker
3334
compilerVersion: "Swift 99",
3435
mockSerializedGraphVersion: alteredVersion)
3536
do {
37+
let outputFileMap = OutputFileMap.mock(maxIndex: Self.maxIndex)
3638
_ = try ModuleDependencyGraph.read(from: mockPath,
37-
info: .mock(fileSystem: fs))
39+
info: .mock(outputFileMap: outputFileMap, fileSystem: fs))
3840
XCTFail("Should have thrown an exception")
3941
}
4042
catch let ModuleDependencyGraph.ReadError.mismatchedSerializedGraphVersion(expected, read) {
@@ -51,8 +53,9 @@ class DependencyGraphSerializationTests: XCTestCase, ModuleDependencyGraphMocker
5153
let fs = InMemoryFileSystem()
5254
try graph.write(to: mockPath, on: fs, compilerVersion: "Swift 99")
5355

56+
let outputFileMap = OutputFileMap.mock(maxIndex: Self.maxIndex)
5457
let deserializedGraph = try ModuleDependencyGraph.read(from: mockPath,
55-
info: .mock(fileSystem: fs))!
58+
info: .mock(outputFileMap: outputFileMap, fileSystem: fs))!
5659
var originalNodes = Set<ModuleDependencyGraph.Node>()
5760
graph.nodeFinder.forEachNode {
5861
originalNodes.insert($0)

Tests/SwiftDriverTests/Helpers/MockingIncrementalCompilation.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,18 @@ extension BuildRecordInfo {
129129
extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
130130
static func mock(
131131
options: IncrementalCompilationState.Options = [.verifyDependencyGraphAfterEveryImport],
132-
outputFileMap: OutputFileMap = OutputFileMap(),
132+
outputFileMap: OutputFileMap,
133133
fileSystem: FileSystem = localFileSystem,
134134
diagnosticEngine: DiagnosticsEngine = DiagnosticsEngine()
135135
) -> Self {
136136
let diagnosticsEngine = DiagnosticsEngine()
137-
return Self(options, outputFileMap,
137+
// Must set input files in order to avoid graph deserialization from culling
138+
let inputFiles = outputFileMap.entries.keys
139+
.filter {VirtualPath.lookup($0).extension == FileType.swift.rawValue }
140+
.map {TypedVirtualPath(file: $0, type: .swift)}
141+
return Self(options, outputFileMap,
138142
BuildRecordInfo.mock(diagnosticsEngine, outputFileMap),
139-
nil, nil, [], fileSystem,
143+
nil, nil, inputFiles, fileSystem,
140144
diagnosticsEngine)
141145
}
142146
}
@@ -176,7 +180,7 @@ struct MockModuleDependencyGraphCreator {
176180

177181

178182
extension OutputFileMap {
179-
fileprivate static func mock(maxIndex: Int) -> Self {
183+
static func mock(maxIndex: Int) -> Self {
180184
OutputFileMap( entries: (0...maxIndex) .reduce(into: [:]) {
181185
entries, index in
182186
let inputHandle = TypedVirtualPath(mockInput: index).file.intern()

Tests/SwiftDriverTests/IncrementalCompilationTests.swift

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ extension IncrementalCompilationTests {
798798
fingerprintChanged(.interface, "main")
799799
fingerprintChanged(.implementation, "main")
800800

801-
let affectedInputs = removeInputFromInvocation && !afterRestoringBadPriors
801+
let affectedInputs = removeInputFromInvocation
802802
? ["other"]
803803
: [removedInput, "other"]
804804
for input in affectedInputs {
@@ -810,10 +810,6 @@ extension IncrementalCompilationTests {
810810
? nil : input)
811811
}
812812
}
813-
if removeInputFromInvocation && afterRestoringBadPriors {
814-
failedToFindSource(removedInput)
815-
failedToReadSomeSource(compiling: "main")
816-
}
817813
let affectedInputsInBuild = affectedInputs.filter(inputs.contains)
818814
queuingLater(affectedInputsInBuild)
819815
schedulingInvalidated(affectedInputsInBuild)
@@ -826,27 +822,6 @@ extension IncrementalCompilationTests {
826822
let graph = try driver.moduleDependencyGraph()
827823
graph.verifyGraph()
828824
if removeInputFromInvocation {
829-
if afterRestoringBadPriors {
830-
// FIXME: Fix the driver
831-
// If you incrementally compile with a.swift and b.swift,
832-
// at the end, the driver saves a serialized `ModuleDependencyGraph`
833-
// contains nodes for declarations defined in both files.
834-
// If you then later remove b.swift and recompile, the driver will
835-
// see that a file was removed (via comparisons with the saved `BuildRecord`
836-
// and will delete the saved priors. However, if for some reason the
837-
// saved priors are not deleted, the driver will read saved priors
838-
// containing entries for the deleted file. This test simulates that
839-
// condition by restoring the deleted priors. The driver ought to be fixed
840-
// to cull any entries for removed files from the deserialized priors.
841-
//
842-
// There is a wrinkle: How can a node for a decl in a removed file be
843-
// distinguished from a node for a decl in an incrementally-imported
844-
// external dependency? In both cases the node's `dependencySource`
845-
// is for a file that is not in the `inputFiles`.
846-
print("*** WARNING: skipping checks, driver fails to cleaned out the graph ***",
847-
to: &stderrStream); stderrStream.flush()
848-
return graph
849-
}
850825
graph.ensureOmits(sourceBasenameWithoutExt: removedInput)
851826
graph.ensureOmits(name: topLevelName)
852827
}

0 commit comments

Comments
 (0)