Skip to content

Conversation

@davidungar
Copy link
Contributor

This is a bit of a big PR, sorry. It's the driver's dependency graph, along with the unit tests, which run!

@davidungar
Copy link
Contributor Author

@swift-ci please test

let swiftDeps: String
let destination: ModuleDependencyGraph

/// When done, changeDependencyKeys contains a list of keys that changed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment is stale, I do not see changeDependencyKeys in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll update it.

///
/// Use a class, not a struct because otherwise it would be duplicated for each thing it uses

@_spi(Testing) public final class ModuleDepGraphNode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to need reference semantics.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right! It doesn't. I'm worried about the memory footprint of using a struct. Each Decl and each use of a Decl would need a copy. If someone has a huge module it might add up.
Or is it still reasonable?

// MARK: - comparing, hashing
extension ModuleDepGraphNode: Equatable, Hashable {
/// Excludes hasBeenTraced...
static public func == (lhs: ModuleDepGraphNode, rhs: ModuleDepGraphNode) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: access level first.

public static func

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, can this conformance be derived?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Will fix.

The compiler doesn't seem to derive that conformance for a class.

&& lhs.swiftDeps == rhs.swiftDeps
}

public func hash(into hasher: inout Hasher) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be derived?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to like deriving those conformance is for classes.

@@ -0,0 +1,101 @@
//===------------------------- Multidictionary.swift ----------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filename has typo Multdictionary. Missing i

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@CodaFi
Copy link
Contributor

CodaFi commented Oct 1, 2020

What would y'all say to splitting this into its own package target (say, SwiftIncremental or just Incremental)? It would let us better hide the implementation details of incremental mode from the rest of the driver.

}
}

private func integrateANewDef(_ integrand: SourceFileDependencyGraph.Node)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This has a bunch of needless words. integrate(definition:) reads better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can eliminate the "A", but "new" and "def" are significant here. It's integrating something new to the graph, and it's a def, not a use. But if you have something else equally expressive...

@CodaFi
Copy link
Contributor

CodaFi commented Oct 1, 2020

The indentation in all of these files is very higgledy-piggledy. Could you try to match the style of the rest of the driver?

/// Returns a nil graph if there's a error
private static func getSourceFileDependencyGraphs(job: Job,
diagnosticEngine: DiagnosticsEngine)
-> [(graph: SourceFileDependencyGraph?, swiftDeps: String)]
Copy link
Contributor

@CodaFi CodaFi Oct 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is crying out for a type.

private enum LoadedDependencyGraph { case success(SourceFileDependencyGraph, String) case failure(String) } 
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

return nil
}

let (foundChange: foundChange, integratedNode: integratedNode) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use a plain tuple here instead of a shuffle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean not immediately decompose the tuple? If so, what's a good name for the whole tuple? I'm having trouble coming up with one.

func testBasicLoad() {
let graph = ModuleDependencyGraph(mock: de)

graph.simulateLoad(job0, [.topLevel: ["a->", "b->"]])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much as I think the DSL you built for dependency edges is cute, I would like us to rebuild this with something more maintainable at some point in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

import Foundation
import TSCBasic

import Foundation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to import Foundation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and not TSCBasic, either. Both gone. I could say I was importing it twice to be sure everything got imported, but that would just be a bad joke.

private var jobsBySwiftDeps: [String: Job] = [:]


func getJob(_ swiftDeps: String) -> Job {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like a stronger type than String for the SwiftDeps file here and in the integrator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, good idea! Excellent suggestion.

/// The former comes from a frontend job, and the latter is used by the driver.
@_spi(Testing) public struct DepGraphIntegrator {
/// nil means there was an error
@_spi(Testing) public typealias Changes = Set<ModuleDepGraphNode>?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nit, but I think it may be more-readable to have Changes be non-optional here and then changedNodes can have type Changes and the signature of integrate would return Changes? and convey that the result may be nil to the reader?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Will do.

let (foundChange: foundChange, integratedNode: integratedNode) =
integrate(integrand, reconcilingWith: preexistingMatchHereOrExpat)

let hasNewExternalDependency = recordWhatIsDependendedUpon(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Dependended

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

switch (lhs, rhs) {
case (.end, .end): return false
case (_, .end): return true
case (end, _): return false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing .?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Yet it compiled and ran...?

Copy link
Contributor

@owenv owenv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have much to add to the other reviews, but it's really great to see this coming together! Once the organization is final all the new files should be added to the CMake build too.

}
// MARK: - comparing, hashing
extension ModuleDepGraphNode: Equatable, Hashable {
/// Excludes hasBeenTraced...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Looks like this comment might not be relevant to the new implementation

@owenv
Copy link
Contributor

owenv commented Oct 3, 2020

What would y'all say to splitting this into its own package target (say, SwiftIncremental or just Incremental)? It would let us better hide the implementation details of incremental mode from the rest of the driver.

It would be nice to hide some of implementation details, but I think the use of Job here makes that difficult. We'd probably want SwiftDriver to import SwiftIncremental as @_implementationOnly, which would require a ton of refactoring to avoid referencing Job in SwiftIncremental, or moving Job, VirtualPath, etc. to another new package target that both higher-level targets could import. Maybe I'm overthinking this though?

Copy link
Contributor Author

@davidungar davidungar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have much to add to the other reviews, but it's really great to see this coming together! Once the organization is final all the new files should be added to the CMake build too.

Thanks! Yes, thank you for that reminder. (I don't understand how I got into review mode!)

}
}

private func integrateANewDef(_ integrand: SourceFileDependencyGraph.Node)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can eliminate the "A", but "new" and "def" are significant here. It's integrating something new to the graph, and it's a def, not a use. But if you have something else equally expressive...

switch (lhs, rhs) {
case (.end, .end): return false
case (_, .end): return true
case (end, _): return false
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Yet it compiled and ran...?

import Foundation
import TSCBasic

import Foundation
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and not TSCBasic, either. Both gone. I could say I was importing it twice to be sure everything got imported, but that would just be a bad joke.

private var jobsBySwiftDeps: [String: Job] = [:]


func getJob(_ swiftDeps: String) -> Job {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, good idea! Excellent suggestion.

func testBasicLoad() {
let graph = ModuleDependencyGraph(mock: de)

graph.simulateLoad(job0, [.topLevel: ["a->", "b->"]])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

@davidungar
Copy link
Contributor Author

@DougGregor @CodaFi @artemcm At this point, I think I've addressed the great suggestions. Would you care to have a look? TIA.

@davidungar
Copy link
Contributor Author

@swift-ci please test

Copy link
Contributor

@artemcm artemcm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing all the comments!

@artemcm
Copy link
Contributor

artemcm commented Oct 6, 2020

@swift-ci please test

@CodaFi
Copy link
Contributor

CodaFi commented Oct 7, 2020

It would be nice to hide some of implementation details

A protocol could provide a sufficient abstraction boundary here. We could sink it and common data structures into a Core or Basics library and pull that into both targets.

@davidungar davidungar deleted the module-dep-graph-10-1b branch October 28, 2020 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

5 participants