Skip to content

Commit 7ac0a5b

Browse files
committed
[Request-evaluator] Improve dumping of dependencies and cycles.
When dumping dependencies, clean up the output in two ways: * Elide redundant but (non-cyclic) references to the same dependency. * When dumping a cycle, highlight the path to the cycle so it stands out.
1 parent b1b3c43 commit 7ac0a5b

File tree

3 files changed

+62
-25
lines changed

3 files changed

+62
-25
lines changed

include/swift/AST/Evaluator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@ class Evaluator {
288288
/// the other overload.
289289
void printDependencies(const AnyRequest &request,
290290
llvm::raw_ostream &out,
291-
llvm::DenseSet<AnyRequest> &visited,
291+
llvm::DenseSet<AnyRequest> &visitedAnywhere,
292+
llvm::SmallVectorImpl<AnyRequest> &visitedAlongPath,
293+
llvm::ArrayRef<AnyRequest> highlightPath,
292294
std::string &prefixStr,
293295
bool lastChild) const;
294296

lib/AST/Evaluator.cpp

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,18 @@ bool Evaluator::checkDependency(const AnyRequest &request) {
4646
return false;
4747
}
4848

49+
// Diagnose cycle.
4950
switch (shouldDiagnoseCycles) {
5051
case CycleDiagnosticKind::NoDiagnose:
5152
return true;
5253

5354
case CycleDiagnosticKind::DebugDiagnose: {
54-
llvm::dbgs() << "===CYCLE DETECTED===\n";
55-
llvm::DenseSet<AnyRequest> visited;
55+
llvm::errs() << "===CYCLE DETECTED===\n";
56+
llvm::DenseSet<AnyRequest> visitedAnywhere;
57+
llvm::SmallVector<AnyRequest, 4> visitedAlongPath;
5658
std::string prefixStr;
57-
printDependencies(activeRequests.front(), llvm::dbgs(), visited,
59+
printDependencies(activeRequests.front(), llvm::errs(), visitedAnywhere,
60+
visitedAlongPath, activeRequests.getArrayRef(),
5861
prefixStr, /*lastChild=*/true);
5962
return true;
6063
}
@@ -78,27 +81,52 @@ void Evaluator::diagnoseCycle(const AnyRequest &request) {
7881
llvm_unreachable("Diagnosed a cycle but it wasn't represented in the stack");
7982
}
8083

81-
void Evaluator::printDependencies(const AnyRequest &request,
82-
llvm::raw_ostream &out,
83-
llvm::DenseSet<AnyRequest> &visited,
84-
std::string &prefixStr,
85-
bool lastChild) const {
84+
void Evaluator::printDependencies(
85+
const AnyRequest &request,
86+
llvm::raw_ostream &out,
87+
llvm::DenseSet<AnyRequest> &visitedAnywhere,
88+
llvm::SmallVectorImpl<AnyRequest> &visitedAlongPath,
89+
llvm::ArrayRef<AnyRequest> highlightPath,
90+
std::string &prefixStr,
91+
bool lastChild) const {
8692
out << prefixStr << " `--";
8793

94+
// Determine whether this node should be highlighted.
95+
bool isHighlighted = false;
96+
if (std::find(highlightPath.begin(), highlightPath.end(), request)
97+
!= highlightPath.end()) {
98+
isHighlighted = true;
99+
out.changeColor(llvm::buffer_ostream::Colors::GREEN);
100+
}
101+
88102
// Print this node.
89103
simple_display(out, request);
90104

105+
// Turn off the highlight.
106+
if (isHighlighted) {
107+
out.resetColor();
108+
}
109+
91110
// Print the cached value, if known.
92111
auto cachedValue = cache.find(request);
93112
if (cachedValue != cache.end()) {
94113
out << " -> ";
95114
PrintEscapedString(cachedValue->second.getAsString(), out);
96115
}
97116

98-
if (!visited.insert(request).second) {
99-
// We've already seed this node, so we have a cyclic dependency.
100-
out.changeColor(llvm::raw_ostream::RED);
101-
out << " (cyclic dependency)\n";
117+
if (!visitedAnywhere.insert(request).second) {
118+
// We've already seed this node. Check whether it's part of a cycle.
119+
if (std::find(visitedAlongPath.begin(), visitedAlongPath.end(), request)
120+
!= visitedAlongPath.end()) {
121+
// We have a cyclic dependency.
122+
out.changeColor(llvm::raw_ostream::RED);
123+
out << " (cyclic dependency)\n";
124+
} else {
125+
// We have seen this node before, but it's not a cycle. Elide its
126+
// children.
127+
out << " (elided)\n";
128+
}
129+
102130
out.resetColor();
103131
} else if (dependencies.count(request) == 0) {
104132
// We have not seen this node before, so we don't know its dependencies.
@@ -107,7 +135,7 @@ void Evaluator::printDependencies(const AnyRequest &request,
107135
out.resetColor();
108136

109137
// Remove from the visited set.
110-
visited.erase(request);
138+
visitedAnywhere.erase(request);
111139
} else {
112140
// Print children.
113141
out << "\n";
@@ -117,26 +145,33 @@ void Evaluator::printDependencies(const AnyRequest &request,
117145
prefixStr += (lastChild ? ' ' : '|');
118146
prefixStr += " ";
119147

148+
// Note that this request is along the path.
149+
visitedAlongPath.push_back(request);
150+
120151
// Print the children.
121152
auto &dependsOn = dependencies.find(request)->second;
122153
for (unsigned i : indices(dependsOn)) {
123-
printDependencies(dependsOn[i], out, visited, prefixStr,
124-
i == dependsOn.size()-1);
154+
printDependencies(dependsOn[i], out, visitedAnywhere, visitedAlongPath,
155+
highlightPath, prefixStr, i == dependsOn.size()-1);
125156
}
126157

127158
// Drop our changes to the prefix.
128159
prefixStr.erase(prefixStr.end() - 4, prefixStr.end());
129160

130-
// Remove from the visited set.
131-
visited.erase(request);
161+
// Remove from the visited set and path.
162+
visitedAnywhere.erase(request);
163+
assert(visitedAlongPath.back() == request);
164+
visitedAlongPath.pop_back();
132165
}
133166
}
134167

135168
void Evaluator::printDependencies(const AnyRequest &request,
136169
llvm::raw_ostream &out) const {
137170
std::string prefixStr;
138-
llvm::DenseSet<AnyRequest> visited;
139-
printDependencies(request, out, visited, prefixStr, /*lastChild=*/true);
171+
llvm::DenseSet<AnyRequest> visitedAnywhere;
172+
llvm::SmallVector<AnyRequest, 4> visitedAlongPath;
173+
printDependencies(request, out, visitedAnywhere, visitedAlongPath, { },
174+
prefixStr, /*lastChild=*/true);
140175
}
141176

142177
void Evaluator::dumpDependencies(const AnyRequest &request) const {

test/decl/class/circular_inheritance.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class Outer3
4141
}
4242

4343
// CHECK: ===CYCLE DETECTED===
44-
// CHECK-NEXT: `--SuperclassTypeRequest
45-
// CHECK-NEXT: `--InheritedTypeRequest(circular_inheritance.(file).Left@
46-
// CHECK-NEXT: `--SuperclassTypeRequest
47-
// CHECK-NEXT: `--InheritedTypeRequest(circular_inheritance.(file).Right@
48-
// CHECK-NEXT: `--SuperclassTypeRequest{{.*}}(cyclic dependency)
44+
// CHECK-NEXT: `--{{.*}}SuperclassTypeRequest
45+
// CHECK-NEXT: `--{{.*}}InheritedTypeRequest(circular_inheritance.(file).Left@
46+
// CHECK-NEXT: `--{{.*}}SuperclassTypeRequest
47+
// CHECK-NEXT: `--{{.*}}InheritedTypeRequest(circular_inheritance.(file).Right@
48+
// CHECK-NEXT: `--{{.*}}SuperclassTypeRequest{{.*(cyclic dependency)}}

0 commit comments

Comments
 (0)