Skip to content

Commit 45d6b76

Browse files
author
David Ungar
committed
Merge pull request swiftlang#675 from davidungar/after-replace-bijection
[Incremental] Harness immutability to make `inputDependencySourceMap` robust # Conflicts: # Sources/SwiftDriver/IncrementalCompilation/ModuleDependencyGraph.swift
1 parent d538883 commit 45d6b76

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
@@ -237,9 +237,6 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
237237
else {
238238
return buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
239239
}
240-
guard graph.populateInputDependencySourceMap(for: .inputsAddedSincePriors) else {
241-
return nil
242-
}
243240
graph.dotFileWriter?.write(graph)
244241

245242
// Any externals not already in graph must be additions which should trigger
@@ -264,13 +261,10 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
264261
private func buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
265262
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
266263
{
267-
let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
268-
assert(outputFileMap.onlySourceFilesHaveSwiftDeps())
269-
270-
guard graph.populateInputDependencySourceMap(for: .buildingFromSwiftDeps) else {
264+
guard let graph = ModuleDependencyGraph(self, .buildingWithoutAPrior)
265+
else {
271266
return nil
272267
}
273-
274268
var inputsInvalidatedByChangedExternals = TransitivelyInvalidatedInputSet()
275269
for input in sourceFiles.currentInOrder {
276270
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,
@@ -190,46 +195,9 @@ extension ModuleDependencyGraph {
190195
return nodeFinder.findNodes(for: source).map {!$0.isEmpty}
191196
?? false
192197
}
193-
194-
/// - Returns: false on error
195-
func populateInputDependencySourceMap(
196-
`for` purpose: InputDependencySourceMap.AdditionPurpose
197-
) -> Bool {
198-
let ofm = info.outputFileMap
199-
let diags = info.diagnosticEngine
200-
var allFound = true
201-
for input in info.inputFiles {
202-
if let source = ofm.dependencySource(for: input, diagnosticEngine: diags) {
203-
inputDependencySourceMap.addEntry(input, source, for: purpose)
204-
} else {
205-
// Don't break in order to report all failures.
206-
allFound = false
207-
}
208-
}
209-
return allFound
210-
}
211-
}
212-
extension OutputFileMap {
213-
fileprivate func dependencySource(
214-
for sourceFile: TypedVirtualPath,
215-
diagnosticEngine: DiagnosticsEngine
216-
) -> DependencySource? {
217-
assert(sourceFile.type == FileType.swift)
218-
guard let swiftDepsPath = existingOutput(inputFile: sourceFile.fileHandle,
219-
outputType: .swiftDeps)
220-
else {
221-
// The legacy driver fails silently here.
222-
diagnosticEngine.emit(
223-
.remarkDisabled("\(sourceFile.file.basename) has no swiftDeps file")
224-
)
225-
return nil
226-
}
227-
assert(VirtualPath.lookup(swiftDepsPath).extension == FileType.swiftDeps.rawValue)
228-
let typedSwiftDepsFile = TypedVirtualPath(file: swiftDepsPath, type: .swiftDeps)
229-
return DependencySource(typedSwiftDepsFile)
230-
}
231198
}
232199

200+
233201
// MARK: - Scheduling the 2nd wave
234202
extension ModuleDependencyGraph {
235203
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -442,7 +410,6 @@ extension ModuleDependencyGraph {
442410
case useIDNode = 4
443411
case externalDepNode = 5
444412
case identifierNode = 6
445-
case mapNode = 7
446413

447414
/// The human-readable name of this record.
448415
///
@@ -463,8 +430,6 @@ extension ModuleDependencyGraph {
463430
return "EXTERNAL_DEP_NODE"
464431
case .identifierNode:
465432
return "IDENTIFIER_NODE"
466-
case .mapNode:
467-
return "MAP_NODE"
468433
}
469434
}
470435
}
@@ -516,12 +481,15 @@ extension ModuleDependencyGraph {
516481
private var identifiers: [String] = [""]
517482
private var currentDefKey: DependencyKey? = nil
518483
private var nodeUses: [(DependencyKey, Int)] = []
519-
private var inputDependencySourceMap: [(TypedVirtualPath, DependencySource)] = []
520484
public private(set) var allNodes: [Node] = []
521485

522-
init(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
486+
init?(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
523487
self.fileSystem = info.fileSystem
524-
self.graph = ModuleDependencyGraph(info, .updatingFromAPrior)
488+
guard let graph = ModuleDependencyGraph(info, .updatingFromAPrior)
489+
else {
490+
return nil
491+
}
492+
self.graph = graph
525493
}
526494

527495
func finalizeGraph() -> ModuleDependencyGraph {
@@ -530,11 +498,6 @@ extension ModuleDependencyGraph {
530498
.record(def: dependencyKey, use: self.allNodes[useID])
531499
assert(isNewUse, "Duplicate use def-use arc in graph?")
532500
}
533-
for (input, dependencySource) in inputDependencySourceMap {
534-
graph.inputDependencySourceMap.addEntry(input,
535-
dependencySource,
536-
for: .readingPriors)
537-
}
538501
return self.graph
539502
}
540503

@@ -624,27 +587,6 @@ extension ModuleDependencyGraph {
624587
throw ReadError.malformedDependsOnRecord
625588
}
626589
self.nodeUses.append( (key, Int(record.fields[0])) )
627-
case .mapNode:
628-
guard record.fields.count == 2,
629-
record.fields[0] < identifiers.count,
630-
record.fields[1] < identifiers.count
631-
else {
632-
throw ReadError.malformedModuleDepGraphNodeRecord
633-
}
634-
let inputPathString = identifiers[Int(record.fields[0])]
635-
let dependencySourcePathString = identifiers[Int(record.fields[1])]
636-
let inputHandle = try VirtualPath.intern(path: inputPathString)
637-
let inputPath = VirtualPath.lookup(inputHandle)
638-
let dependencySourceHandle = try VirtualPath.intern(path: dependencySourcePathString)
639-
let dependencySourcePath = VirtualPath.lookup(dependencySourceHandle)
640-
guard inputPath.extension == FileType.swift.rawValue,
641-
dependencySourcePath.extension == FileType.swiftDeps.rawValue,
642-
let dependencySource = DependencySource(dependencySourceHandle)
643-
else {
644-
throw ReadError.malformedMapRecord
645-
}
646-
let input = TypedVirtualPath(file: inputHandle, type: .swift)
647-
inputDependencySourceMap.append((input, dependencySource))
648590
case .externalDepNode:
649591
guard record.fields.count == 2,
650592
record.fields[0] < identifiers.count,
@@ -670,7 +612,9 @@ extension ModuleDependencyGraph {
670612
}
671613
}
672614

673-
var visitor = Visitor(info)
615+
guard var visitor = Visitor(info) else {
616+
return nil
617+
}
674618
try Bitcode.read(bytes: data, using: &visitor)
675619
guard let major = visitor.majorVersion,
676620
let minor = visitor.minorVersion,
@@ -768,7 +712,6 @@ extension ModuleDependencyGraph {
768712
self.emitRecordID(.useIDNode)
769713
self.emitRecordID(.externalDepNode)
770714
self.emitRecordID(.identifierNode)
771-
self.emitRecordID(.mapNode)
772715
}
773716
}
774717

@@ -830,11 +773,6 @@ extension ModuleDependencyGraph {
830773
}
831774
}
832775

833-
graph.inputDependencySourceMap.enumerateToSerializePriors { input, dependencySource in
834-
self.addIdentifier(input.file.name)
835-
self.addIdentifier(dependencySource.file.name)
836-
}
837-
838776
for edF in graph.fingerprintedExternalDependencies {
839777
self.addIdentifier(edF.externalDependency.fileName)
840778
}
@@ -906,13 +844,6 @@ extension ModuleDependencyGraph {
906844
// identifier data
907845
.blob
908846
])
909-
self.abbreviate(.mapNode, [
910-
.literal(RecordID.mapNode.rawValue),
911-
// input name
912-
.vbr(chunkBitWidth: 13),
913-
// dependencySource name
914-
.vbr(chunkBitWidth: 13),
915-
])
916847
}
917848

918849
private func abbreviate(
@@ -975,15 +906,6 @@ extension ModuleDependencyGraph {
975906
}
976907
}
977908
}
978-
graph.inputDependencySourceMap.enumerateToSerializePriors {
979-
input, dependencySource in
980-
serializer.stream.writeRecord(serializer.abbreviations[.mapNode]!) {
981-
$0.append(RecordID.mapNode)
982-
$0.append(serializer.lookupIdentifierCode(for: input.file.name))
983-
$0.append(serializer.lookupIdentifierCode(for: dependencySource.file.name))
984-
}
985-
}
986-
987909
for fingerprintedExternalDependency in graph.fingerprintedExternalDependencies {
988910
serializer.stream.writeRecord(serializer.abbreviations[.externalDepNode]!, {
989911
$0.append(RecordID.externalDepNode)
@@ -1079,7 +1001,6 @@ fileprivate extension DependencyKey.Designator {
10791001
extension ModuleDependencyGraph {
10801002
func matches(_ other: ModuleDependencyGraph) -> Bool {
10811003
guard nodeFinder.matches(other.nodeFinder),
1082-
inputDependencySourceMap.matches(other.inputDependencySourceMap),
10831004
fingerprintedExternalDependencies.matches(other.fingerprintedExternalDependencies)
10841005
else {
10851006
return false
@@ -1094,24 +1015,8 @@ extension Set where Element == ModuleDependencyGraph.Node {
10941015
}
10951016
}
10961017

1097-
extension InputDependencySourceMap {
1098-
fileprivate func matches(_ other: Self) -> Bool {
1099-
self == other
1100-
}
1101-
}
1102-
11031018
extension Set where Element == FingerprintedExternalDependency {
11041019
fileprivate func matches(_ other: Self) -> Bool {
11051020
self == other
11061021
}
11071022
}
1108-
1109-
/// This should be in a test file, but addMapEntry should be private.
1110-
extension ModuleDependencyGraph {
1111-
@_spi(Testing) public func mockMapEntry(
1112-
_ mockInput: TypedVirtualPath,
1113-
_ mockDependencySource: DependencySource
1114-
) {
1115-
inputDependencySourceMap.addEntry(mockInput, mockDependencySource, for: .mocking)
1116-
}
1117-
}

0 commit comments

Comments
 (0)