Skip to content

Commit 13023de

Browse files
committed
[Evaluator] Avoid emitting duplicate "through reference here" notes
Filter out any duplicate notes to help cut down on the noise for request cycle diagnostics. Some of the note locations here still aren't great, but this at least stops us from repeating them for each intermediate request.
1 parent 382a52e commit 13023de

File tree

17 files changed

+108
-68
lines changed

17 files changed

+108
-68
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,13 @@ namespace swift {
14621462
/// Retrieve the underlying engine which will receive the diagnostics.
14631463
DiagnosticEngine &getUnderlyingDiags() const { return UnderlyingEngine; }
14641464

1465+
/// Iterates over each captured diagnostic, running a lambda with it.
1466+
void forEach(llvm::function_ref<void(const Diagnostic &)> body) const;
1467+
1468+
/// Filters the queued diagnostics, dropping any where the predicate
1469+
/// returns \c false.
1470+
void filter(llvm::function_ref<bool(const Diagnostic &)> predicate);
1471+
14651472
/// Clear this queue and erase all diagnostics recorded.
14661473
void clear() {
14671474
assert(QueueEngine.TransactionCount == 1 &&

lib/AST/DiagnosticEngine.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,20 @@ void DiagnosticEngine::onTentativeDiagnosticFlush(Diagnostic &diagnostic) {
17841784
}
17851785
}
17861786

1787+
void DiagnosticQueue::forEach(
1788+
llvm::function_ref<void(const Diagnostic &)> body) const {
1789+
for (auto &activeDiag : QueueEngine.TentativeDiagnostics)
1790+
body(activeDiag.Diag);
1791+
}
1792+
1793+
void DiagnosticQueue::filter(
1794+
llvm::function_ref<bool(const Diagnostic &)> predicate) {
1795+
llvm::erase_if(QueueEngine.TentativeDiagnostics,
1796+
[&](detail::ActiveDiagnostic &activeDiag) {
1797+
return !predicate(activeDiag.Diag);
1798+
});
1799+
}
1800+
17871801
EncodedDiagnosticMessage::EncodedDiagnosticMessage(StringRef S)
17881802
: Message(Lexer::getEncodedStringSegment(S, Buf, /*IsFirstSegment=*/true,
17891803
/*IsLastSegment=*/true,

lib/AST/Evaluator.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/DiagnosticEngine.h"
2020
#include "swift/AST/TypeCheckRequests.h" // for ResolveMacroRequest
2121
#include "swift/Basic/Assertions.h"
22+
#include "swift/Basic/Defer.h"
2223
#include "swift/Basic/LangOptions.h"
2324
#include "swift/Basic/Range.h"
2425
#include "swift/Basic/SourceManager.h"
@@ -123,17 +124,40 @@ void Evaluator::diagnoseCycle(const ActiveRequest &request) {
123124
OS << "\n";
124125
}
125126

126-
request.diagnoseCycle(diags);
127-
for (const auto &step : llvm::reverse(activeRequests)) {
128-
if (step == request) return;
129-
130-
// Reporting the lifetime dependence location generates a redundant
131-
// diagnostic.
132-
if (step.getAs<LifetimeDependenceInfoRequest>()) {
133-
continue;
134-
}
127+
// Perform some filtering to avoid emitting duplicate 'through reference here'
128+
// notes.
129+
DiagnosticQueue cycleDiags(diags, /*emitOnDestruction*/ true);
130+
SWIFT_DEFER {
131+
auto isDefaultStepNote = [](const Diagnostic &diag) -> bool {
132+
return diag.getID() == diag::circular_reference_through.ID;
133+
};
134+
// First populate seen locs for everything except default step notes. If
135+
// we have a diagnostic or custom note at a given location we want to prefer
136+
// that over the default step note.
137+
llvm::DenseSet<SourceLoc> seenLocs;
138+
cycleDiags.forEach([&](const Diagnostic &diag) {
139+
if (isDefaultStepNote(diag))
140+
return;
141+
142+
if (auto loc = diag.getLocOrDeclLoc())
143+
seenLocs.insert(loc);
144+
});
145+
// Then we can filter out unnecessary default step notes.
146+
cycleDiags.filter([&](const Diagnostic &diag) -> bool {
147+
if (!isDefaultStepNote(diag))
148+
return true;
149+
150+
auto loc = diag.getLocOrDeclLoc();
151+
return loc && seenLocs.insert(loc).second;
152+
});
153+
};
154+
155+
request.diagnoseCycle(cycleDiags.getDiags());
135156

136-
step.noteCycleStep(diags);
157+
for (const auto &step : llvm::reverse(activeRequests)) {
158+
if (step == request)
159+
return;
160+
step.noteCycleStep(cycleDiags.getDiags());
137161
}
138162

139163
llvm_unreachable("Diagnosed a cycle but it wasn't represented in the stack");
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
typealias A = B // expected-error {{type alias 'A' references itself}} expected-note {{while resolving type 'B'}} expected-note {{through reference here}}
4-
typealias C = D // expected-note {{through reference here}} expected-note {{while resolving type 'D'}} expected-note {{through reference here}}
5-
typealias D = (A, Int) // expected-note {{through reference here}} expected-note {{while resolving type '(A, Int)'}} expected-note {{through reference here}}
6-
typealias B = C // expected-note {{through reference here}} expected-note {{while resolving type 'C'}} expected-note {{through reference here}}
3+
typealias A = B // expected-error {{type alias 'A' references itself}} expected-note {{while resolving type 'B'}}
4+
typealias C = D // expected-note {{through reference here}} expected-note {{while resolving type 'D'}}
5+
typealias D = (A, Int) // expected-note {{through reference here}} expected-note {{while resolving type '(A, Int)'}}
6+
typealias B = C // expected-note {{through reference here}} expected-note {{while resolving type 'C'}}

test/Generics/issue-69318.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public protocol ChildlessView: View where NodeChildren == EmptyViewGraphNodeChil
1515
// expected-error@-1 {{circular reference}}
1616

1717
public struct EmptyViewGraphNodeChildren<ParentView: ChildlessView>: ViewGraphNodeChildren {}
18-
// expected-note@-1 3{{through reference here}}
19-
// expected-error@-2 {{type 'EmptyViewGraphNodeChildren<ParentView>' does not conform to protocol 'ViewGraphNodeChildren'}}
20-
// expected-note@-3 {{add stubs for conformance}}
18+
// expected-note@-1:15 {{through reference here}}
19+
// expected-note@-2:70 {{through reference here}}
20+
// expected-error@-3 {{type 'EmptyViewGraphNodeChildren<ParentView>' does not conform to protocol 'ViewGraphNodeChildren'}}
21+
// expected-note@-4 {{add stubs for conformance}}

test/Generics/rdar123013710.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ public protocol P {
88
static func f() -> Any?
99
}
1010

11-
protocol Q: P where B == Never {} // expected-error {{circular reference}}
11+
protocol Q: P where B == Never {} // expected-error@:10 {{circular reference}}
1212

13-
extension Never: Q, P { // expected-note 2{{through reference here}}
13+
extension Never: Q, P { // expected-note@:1 {{through reference here}}
1414
public typealias A = Never
1515
public static func f() -> Any? { nil }
1616
}

test/Generics/rdar94848868.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
// This is too circular to work, but it shouldn't crash.
44

55
protocol MyCollectionProtocol: Collection where Iterator == MyCollectionIterator<Self> {}
6-
// expected-error@-1 {{circular reference}}
6+
// expected-error@-1:10 {{circular reference}}
77

88
struct MyCollectionIterator<MyCollection: MyCollectionProtocol>: IteratorProtocol {
9-
// expected-note@-1 3{{through reference here}}
10-
// expected-error@-2 {{type 'MyCollectionIterator<MyCollection>' does not conform to protocol 'IteratorProtocol'}}
11-
// expected-note@-3 {{add stubs for conformance}}
9+
// expected-note@-1:8 {{through reference here}}
10+
// expected-note@-2:66 {{through reference here}}
11+
// expected-error@-3 {{type 'MyCollectionIterator<MyCollection>' does not conform to protocol 'IteratorProtocol'}}
12+
// expected-note@-4 {{add stubs for conformance}}
1213
mutating func next() -> MyCollection.Element? {
1314
// expected-error@-1 {{'Element' is not a member type of type 'MyCollection'}}
1415
return nil

test/NameLookup/name_lookup.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -637,17 +637,14 @@ struct PatternBindingWithTwoVars2 { var x = y, y = 3 }
637637
// expected-error@-1 {{cannot use instance member 'y' within property initializer; property initializers run before 'self' is available}}
638638

639639
struct PatternBindingWithTwoVars3 { var x = y, y = x }
640-
// expected-error@-1 {{circular reference}}
641-
// expected-note@-2 {{through reference here}}
642-
// expected-note@-3 {{through reference here}}
643-
// expected-note@-4 {{through reference here}}
644-
// expected-note@-5 {{through reference here}}
645-
// expected-note@-6 {{through reference here}}
640+
// expected-error@-1:41 {{circular reference}}
641+
// expected-note@-2:37 {{through reference here}}
642+
// expected-note@-3:48 {{through reference here}}
646643

647644
// https://github.com/apple/swift/issues/51518
648645
do {
649-
let closure1 = { closure2() } // expected-error {{circular reference}} expected-note {{through reference here}} expected-note {{through reference here}}
650-
let closure2 = { closure1() } // expected-note {{through reference here}} expected-note {{through reference here}} expected-note {{through reference here}}
646+
let closure1 = { closure2() } // expected-error {{circular reference}} expected-note {{through reference here}}
647+
let closure2 = { closure1() } // expected-note {{through reference here}} expected-note {{through reference here}}
651648
}
652649

653650
func color(with value: Int) -> Int {

test/Sema/diag_typealias.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
struct S {}
44

5-
typealias S = S // expected-error {{type alias 'S' references itself}} expected-note {{while resolving type 'S'}} expected-note {{through reference here}}
5+
typealias S = S // expected-error {{type alias 'S' references itself}} expected-note {{while resolving type 'S'}}

test/decl/circularity.swift

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,11 @@ class C1 {
6868
func run(a: Int) {}
6969
}
7070

71-
class C2: C1, P {
72-
// expected-note@-1 2{{through reference here}}
71+
class C2: C1, P { // expected-note@:7 {{through reference here}}
7372
override func run(a: A) {}
74-
// expected-error@-1 {{circular reference}}
75-
// expected-note@-2 {{while resolving type 'A'}}
76-
// expected-note@-3 2{{through reference here}}
73+
// expected-error@-1:19 {{circular reference}}
74+
// expected-note@-2:26 {{while resolving type 'A'}}
75+
// expected-note@-3:23 {{through reference here}}
7776
}
7877

7978
// Another crash to the above
@@ -84,12 +83,12 @@ open class G1<A> {
8483
class C3: G1<A>, P {
8584
// expected-error@-1 {{type 'C3' does not conform to protocol 'P'}}
8685
// expected-error@-2 {{cannot find type 'A' in scope}}
87-
// expected-note@-3 2{{through reference here}}
86+
// expected-note@-3:7 {{through reference here}}
8887
// expected-note@-4 {{add stubs for conformance}}
8988
override func run(a: A) {}
90-
// expected-error@-1 {{circular reference}}
91-
// expected-note@-2 2 {{through reference here}}
92-
// expected-note@-3 {{while resolving type 'A'}}
89+
// expected-error@-1:19 {{circular reference}}
90+
// expected-note@-2:26 {{while resolving type 'A'}}
91+
// expected-note@-3:23 {{through reference here}}
9392
}
9493

9594
// Another case that triggers circular override checking.
@@ -102,10 +101,10 @@ class C4 {
102101
required init(x: Int) {}
103102
}
104103

105-
class D4 : C4, P1 { // expected-note 4 {{through reference here}}
106-
required init(x: X) { // expected-error {{circular reference}}
107-
// expected-note@-1 {{while resolving type 'X'}}
108-
// expected-note@-2 2{{through reference here}}
104+
class D4 : C4, P1 { // expected-note@:7 {{through reference here}}
105+
required init(x: X) { // expected-error@:12 {{circular reference}}
106+
// expected-note@-1:20 {{while resolving type 'X'}}
107+
// expected-note@-2:17 {{through reference here}}
109108
super.init(x: x)
110109
}
111110
}
@@ -114,6 +113,6 @@ class D4 : C4, P1 { // expected-note 4 {{through reference here}}
114113
// N.B. This used to compile in 5.1.
115114
protocol P_54662 { }
116115
class C_54662 { // expected-note {{through reference here}}
117-
typealias Nest = P_54662 // expected-error {{circular reference}} expected-note {{through reference here}}
116+
typealias Nest = P_54662 // expected-error {{circular reference}}
118117
}
119118
extension C_54662: C_54662.Nest { }

0 commit comments

Comments
 (0)