Skip to content

Commit f32e8e1

Browse files
author
David Ungar
authored
Merge pull request #675 from davidungar/after-replace-bijection
[Incremental] Harness immutability to make `inputDependencySourceMap` robust
2 parents 6cfb030 + 4887e02 commit f32e8e1

File tree

8 files changed

+183
-223
lines changed

8 files changed

+183
-223
lines changed

Sources/SwiftDriver/IncrementalCompilation/BidirectionalMap.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212

1313
/// Like a two-way dictionary
1414
///
15-
// FIXME: The current use of this abstraction in the driver is
16-
// fundamentally broken. This data structure should be retired ASAP.
17-
// See the extended FIXME in its use in
18-
// `ModuleDependencyGraph.inputDependencySourceMap`
1915
public struct BidirectionalMap<T1: Hashable, T2: Hashable>: Equatable, Sequence {
2016
private var map1: [T1: T2] = [:]
2117
private var map2: [T2: T1] = [:]
@@ -74,6 +70,20 @@ public struct BidirectionalMap<T1: Hashable, T2: Hashable>: Equatable, Sequence
7470
public func contains(key: T2) -> Bool {
7571
map2.keys.contains(key)
7672
}
73+
74+
public mutating func updateValue(_ newValue: T2, forKey key: T1) -> T2? {
75+
let oldValue = map1.updateValue(newValue, forKey: key)
76+
_ = oldValue.map {map2.removeValue(forKey: $0)}
77+
map2[newValue] = key
78+
return oldValue
79+
}
80+
public mutating func updateValue(_ newValue: T1, forKey key: T2) -> T1? {
81+
let oldValue = map2.updateValue(newValue, forKey: key)
82+
_ = oldValue.map {map1.removeValue(forKey: $0)}
83+
map1[newValue] = key
84+
return oldValue
85+
}
86+
7787
public mutating func removeValue(forKey t1: T1) {
7888
if let t2 = map1[t1] {
7989
map2.removeValue(forKey: t2)

Sources/SwiftDriver/IncrementalCompilation/DirectAndTransitiveCollections.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public struct Directly {}
1919
public struct InvalidatedSet<ClosureLevel, Element: Hashable>: Sequence {
2020
var contents: Set<Element>
2121

22-
init(_ s: Set<Element> = Set()) {
22+
@_spi(Testing) public init(_ s: Set<Element> = Set()) {
2323
self.contents = s
2424
}
2525
init<Elements: Sequence>(_ elements: Elements)

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,6 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
227227
else {
228228
return buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
229229
}
230-
guard graph.populateInputDependencySourceMap(for: .inputsAddedSincePriors) else {
231-
return nil
232-
}
233230
graph.dotFileWriter?.write(graph)
234231

235232
// Any externals not already in graph must be additions which should trigger
@@ -255,13 +252,10 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
255252
private func buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
256253
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
257254
{
258-
let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
259-
assert(outputFileMap.onlySourceFilesHaveSwiftDeps())
260-
261-
guard graph.populateInputDependencySourceMap(for: .buildingFromSwiftDeps) else {
255+
guard let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
256+
else {
262257
return nil
263258
}
264-
265259
var inputsInvalidatedByChangedExternals = TransitivelyInvalidatedInputSet()
266260
for input in sourceFiles.currentInOrder {
267261
guard let invalidatedInputs =

Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift

Lines changed: 17 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import SwiftOptions
2424
@_spi(Testing) public var nodeFinder = NodeFinder()
2525

2626
/// Maps input files (e.g. .swift) to and from the DependencySource object.
27-
@_spi(Testing) public private(set) var inputDependencySourceMap = InputDependencySourceMap()
27+
@_spi(Testing) public let inputDependencySourceMap: InputDependencySourceMap
2828

2929
// The set of paths to external dependencies known to be in the graph
3030
public internal(set) var fingerprintedExternalDependencies = Set<FingerprintedExternalDependency>()
@@ -43,7 +43,7 @@ import SwiftOptions
4343
/// Minimize the number of file system modification-time queries.
4444
private var externalDependencyModTimeCache = [ExternalDependency: Bool]()
4545

46-
public init(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup,
46+
public init?(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup,
4747
_ phase: Phase
4848
) {
4949
self.info = info
@@ -52,6 +52,11 @@ import SwiftOptions
5252
: nil
5353
self.phase = phase
5454
self.creationPhase = phase
55+
guard let inputDependencySourceMap = InputDependencySourceMap(info)
56+
else {
57+
return nil
58+
}
59+
self.inputDependencySourceMap = inputDependencySourceMap
5560
}
5661

5762
@_spi(Testing) public func sourceRequired(for input: TypedVirtualPath,
@@ -188,46 +193,9 @@ extension ModuleDependencyGraph {
188193
return nodeFinder.findNodes(for: source).map {!$0.isEmpty}
189194
?? false
190195
}
191-
192-
/// Returns: false on error
193-
func populateInputDependencySourceMap(
194-
`for` purpose: InputDependencySourceMap.AdditionPurpose
195-
) -> Bool {
196-
let ofm = info.outputFileMap
197-
let diags = info.diagnosticEngine
198-
var allFound = true
199-
for input in info.inputFiles {
200-
if let source = ofm.dependencySource(for: input, diagnosticEngine: diags) {
201-
inputDependencySourceMap.addEntry(input, source, for: purpose)
202-
} else {
203-
// Don't break in order to report all failures.
204-
allFound = false
205-
}
206-
}
207-
return allFound
208-
}
209-
}
210-
extension OutputFileMap {
211-
fileprivate func dependencySource(
212-
for sourceFile: TypedVirtualPath,
213-
diagnosticEngine: DiagnosticsEngine
214-
) -> DependencySource? {
215-
assert(sourceFile.type == FileType.swift)
216-
guard let swiftDepsPath = existingOutput(inputFile: sourceFile.fileHandle,
217-
outputType: .swiftDeps)
218-
else {
219-
// The legacy driver fails silently here.
220-
diagnosticEngine.emit(
221-
.remarkDisabled("\(sourceFile.file.basename) has no swiftDeps file in the output file map")
222-
)
223-
return nil
224-
}
225-
assert(VirtualPath.lookup(swiftDepsPath).extension == FileType.swiftDeps.rawValue)
226-
let typedSwiftDepsFile = TypedVirtualPath(file: swiftDepsPath, type: .swiftDeps)
227-
return DependencySource(typedSwiftDepsFile)
228-
}
229196
}
230197

198+
231199
// MARK: - Scheduling the 2nd wave
232200
extension ModuleDependencyGraph {
233201
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -489,7 +457,6 @@ extension ModuleDependencyGraph {
489457
case useIDNode = 4
490458
case externalDepNode = 5
491459
case identifierNode = 6
492-
case mapNode = 7
493460

494461
/// The human-readable name of this record.
495462
///
@@ -510,8 +477,6 @@ extension ModuleDependencyGraph {
510477
return "EXTERNAL_DEP_NODE"
511478
case .identifierNode:
512479
return "IDENTIFIER_NODE"
513-
case .mapNode:
514-
return "MAP_NODE"
515480
}
516481
}
517482
}
@@ -563,12 +528,15 @@ extension ModuleDependencyGraph {
563528
private var identifiers: [String] = [""]
564529
private var currentDefKey: DependencyKey? = nil
565530
private var nodeUses: [(DependencyKey, Int)] = []
566-
private var inputDependencySourceMap: [(TypedVirtualPath, DependencySource)] = []
567531
public private(set) var allNodes: [Node] = []
568532

569-
init(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
533+
init?(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
570534
self.fileSystem = info.fileSystem
571-
self.graph = ModuleDependencyGraph(info, .updatingFromAPrior)
535+
guard let graph = ModuleDependencyGraph(info, .updatingFromAPrior)
536+
else {
537+
return nil
538+
}
539+
self.graph = graph
572540
}
573541

574542
func finalizeGraph() -> ModuleDependencyGraph {
@@ -577,11 +545,6 @@ extension ModuleDependencyGraph {
577545
.record(def: dependencyKey, use: self.allNodes[useID])
578546
assert(isNewUse, "Duplicate use def-use arc in graph?")
579547
}
580-
for (input, dependencySource) in inputDependencySourceMap {
581-
graph.inputDependencySourceMap.addEntry(input,
582-
dependencySource,
583-
for: .readingPriors)
584-
}
585548
return self.graph
586549
}
587550

@@ -671,27 +634,6 @@ extension ModuleDependencyGraph {
671634
throw ReadError.malformedDependsOnRecord
672635
}
673636
self.nodeUses.append( (key, Int(record.fields[0])) )
674-
case .mapNode:
675-
guard record.fields.count == 2,
676-
record.fields[0] < identifiers.count,
677-
record.fields[1] < identifiers.count
678-
else {
679-
throw ReadError.malformedModuleDepGraphNodeRecord
680-
}
681-
let inputPathString = identifiers[Int(record.fields[0])]
682-
let dependencySourcePathString = identifiers[Int(record.fields[1])]
683-
let inputHandle = try VirtualPath.intern(path: inputPathString)
684-
let inputPath = VirtualPath.lookup(inputHandle)
685-
let dependencySourceHandle = try VirtualPath.intern(path: dependencySourcePathString)
686-
let dependencySourcePath = VirtualPath.lookup(dependencySourceHandle)
687-
guard inputPath.extension == FileType.swift.rawValue,
688-
dependencySourcePath.extension == FileType.swiftDeps.rawValue,
689-
let dependencySource = DependencySource(dependencySourceHandle)
690-
else {
691-
throw ReadError.malformedMapRecord
692-
}
693-
let input = TypedVirtualPath(file: inputHandle, type: .swift)
694-
inputDependencySourceMap.append((input, dependencySource))
695637
case .externalDepNode:
696638
guard record.fields.count == 2,
697639
record.fields[0] < identifiers.count,
@@ -717,7 +659,9 @@ extension ModuleDependencyGraph {
717659
}
718660
}
719661

720-
var visitor = Visitor(info)
662+
guard var visitor = Visitor(info) else {
663+
return nil
664+
}
721665
try Bitcode.read(bytes: data, using: &visitor)
722666
guard let major = visitor.majorVersion,
723667
let minor = visitor.minorVersion,
@@ -815,7 +759,6 @@ extension ModuleDependencyGraph {
815759
self.emitRecordID(.useIDNode)
816760
self.emitRecordID(.externalDepNode)
817761
self.emitRecordID(.identifierNode)
818-
self.emitRecordID(.mapNode)
819762
}
820763
}
821764

@@ -877,11 +820,6 @@ extension ModuleDependencyGraph {
877820
}
878821
}
879822

880-
graph.inputDependencySourceMap.enumerateToSerializePriors { input, dependencySource in
881-
self.addIdentifier(input.file.name)
882-
self.addIdentifier(dependencySource.file.name)
883-
}
884-
885823
for edF in graph.fingerprintedExternalDependencies {
886824
self.addIdentifier(edF.externalDependency.fileName)
887825
}
@@ -953,13 +891,6 @@ extension ModuleDependencyGraph {
953891
// identifier data
954892
.blob
955893
])
956-
self.abbreviate(.mapNode, [
957-
.literal(RecordID.mapNode.rawValue),
958-
// input name
959-
.vbr(chunkBitWidth: 13),
960-
// dependencySource name
961-
.vbr(chunkBitWidth: 13),
962-
])
963894
}
964895

965896
private func abbreviate(
@@ -1022,15 +953,6 @@ extension ModuleDependencyGraph {
1022953
}
1023954
}
1024955
}
1025-
graph.inputDependencySourceMap.enumerateToSerializePriors {
1026-
input, dependencySource in
1027-
serializer.stream.writeRecord(serializer.abbreviations[.mapNode]!) {
1028-
$0.append(RecordID.mapNode)
1029-
$0.append(serializer.lookupIdentifierCode(for: input.file.name))
1030-
$0.append(serializer.lookupIdentifierCode(for: dependencySource.file.name))
1031-
}
1032-
}
1033-
1034956
for fingerprintedExternalDependency in graph.fingerprintedExternalDependencies {
1035957
serializer.stream.writeRecord(serializer.abbreviations[.externalDepNode]!, {
1036958
$0.append(RecordID.externalDepNode)
@@ -1126,7 +1048,6 @@ fileprivate extension DependencyKey.Designator {
11261048
extension ModuleDependencyGraph {
11271049
func matches(_ other: ModuleDependencyGraph) -> Bool {
11281050
guard nodeFinder.matches(other.nodeFinder),
1129-
inputDependencySourceMap.matches(other.inputDependencySourceMap),
11301051
fingerprintedExternalDependencies.matches(other.fingerprintedExternalDependencies)
11311052
else {
11321053
return false
@@ -1141,24 +1062,8 @@ extension Set where Element == ModuleDependencyGraph.Node {
11411062
}
11421063
}
11431064

1144-
extension InputDependencySourceMap {
1145-
fileprivate func matches(_ other: Self) -> Bool {
1146-
self == other
1147-
}
1148-
}
1149-
11501065
extension Set where Element == FingerprintedExternalDependency {
11511066
fileprivate func matches(_ other: Self) -> Bool {
11521067
self == other
11531068
}
11541069
}
1155-
1156-
/// This should be in a test file, but addMapEntry should be private.
1157-
extension ModuleDependencyGraph {
1158-
@_spi(Testing) public func mockMapEntry(
1159-
_ mockInput: TypedVirtualPath,
1160-
_ mockDependencySource: DependencySource
1161-
) {
1162-
inputDependencySourceMap.addEntry(mockInput, mockDependencySource, for: .mocking)
1163-
}
1164-
}

0 commit comments

Comments
 (0)