Skip to content

Commit b69241e

Browse files
committed
improve diagnosis of bad SuppressedEntries
1 parent 6a153f7 commit b69241e

File tree

3 files changed

+111
-9
lines changed

3 files changed

+111
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6872,6 +6872,14 @@ ERROR(suppress_cannot_suppress,none,
68726872
ERROR(suppress_on_wrong_decl,none,
68736873
"cannot suppress conformance on %0", (DescriptiveDeclKind))
68746874

6875+
ERROR(suppress_duplicate,none,
6876+
"implicit conformance to %0 already suppressed", (Type))
6877+
NOTE(suppress_previously_here,none,
6878+
"%0 previously suppressed here", (Type))
6879+
6880+
ERROR(suppress_not_protocol,none,
6881+
"suppression of non-protocol type %0", (Type))
6882+
68756883
//------------------------------------------------------------------------------
68766884
// MARK: Type inference from default expressions
68776885
//------------------------------------------------------------------------------

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "llvm/ADT/Twine.h"
6666
#include "llvm/Support/Compiler.h"
6767
#include "llvm/Support/DJB.h"
68+
#include <unordered_map>
6869

6970
using namespace swift;
7071

@@ -96,6 +97,13 @@ static void checkSuppressedEntries(
9697
ASTContext &ctx = typeDecl->getASTContext();
9798
auto &diags = ctx.Diags;
9899

100+
using EntryTracker = std::unordered_map<KnownProtocolKind,
101+
SuppressedEntry const *>;
102+
EntryTracker seenEntries;
103+
104+
bool haveMoveOnlyClasses =
105+
typeDecl->getASTContext().LangOpts.hasFeature(Feature::MoveOnlyClasses);
106+
99107
ArrayRef<SuppressedEntry> suppressedEntries = typeDecl->getSuppressed();
100108
for (unsigned i = 0, n = suppressedEntries.size(); i != n; ++i) {
101109
auto &suppressed = suppressedEntries[i];
@@ -108,21 +116,63 @@ static void checkSuppressedEntries(
108116
if (!suppressedTy || suppressedTy->hasError())
109117
continue;
110118

111-
// Skip over suppressible protocols.
119+
// only structs, enums, and some classes can have suppressed entries
120+
if (!(isa<StructDecl>(typeDecl) || isa<EnumDecl>(typeDecl)
121+
|| (isa<ClassDecl>(typeDecl) && haveMoveOnlyClasses))) {
122+
diags.diagnose(suppressed.getLoc(),
123+
diag::suppress_on_wrong_decl,
124+
typeDecl->getDescriptiveKind());
125+
// FIXME: a great fix-it would be to remove the entire entry,
126+
// including any trailing comma. See checkInheritedEntry
127+
// for an example of the logic to do that. You'll need to update it
128+
// using MergedArrayRef so it understands source locations for both
129+
// suppressed and inherited entries.
130+
continue;
131+
}
132+
133+
// Ensure it is a suppressible protocol.
112134
if (auto protocolTy = dyn_cast<ProtocolType>(suppressedTy))
113135
if (auto protocolDecl = protocolTy->getDecl())
114136
if (auto knownProtocol = protocolDecl->getKnownProtocolKind())
115-
if (isSuppressibleProtocol(knownProtocol.getValue()))
116-
continue;
137+
if (isSuppressibleProtocol(*knownProtocol)) {
138+
// remember that we've seen this suppression for the type
139+
EntryTracker::iterator iter;
140+
bool uniqueEntry;
141+
std::tie(iter, uniqueEntry) =
142+
seenEntries.insert({*knownProtocol, &suppressed});
143+
144+
// check if already encountered an entry suppressing this protocol
145+
if (!uniqueEntry) {
146+
assert(suppressedTy->getCanonicalType()
147+
== iter->second->getType()->getCanonicalType());
148+
149+
// Emit a diagnostic about the duplicate suppression.
150+
diags.diagnose(suppressed.getLoc(),
151+
diag::suppress_duplicate,
152+
suppressedTy);
153+
// FIXME: a great fix-it would be to remove the entire entry.
154+
155+
diags.diagnose(iter->second->getLoc(),
156+
diag::suppress_previously_here,
157+
suppressedTy);
158+
}
117159

118-
diags.diagnose(suppressed.getLoc(), diag::suppress_cannot_suppress, suppressedTy);
119-
// TODO: fixit to remove both suppressed TypeLoc up to the next item in
120-
// the inheritance clause list, including a trailing comma, etc.
160+
continue;
161+
}
121162

122-
// TODO: find duplicate suppressions.
163+
// At this point, the suppressed type is wrong. Tailor the error messages.
123164

124-
// TODO: update checkInheritanceClause using MergedArrayRef so it can
125-
// properly locate and understand all of the clauses in its fix-its.
165+
if (suppressedTy->isConstraintType()) {
166+
diags.diagnose(suppressed.getLoc(),
167+
diag::suppress_cannot_suppress,
168+
suppressedTy);
169+
// FIXME: a great fix-it would be to remove the entire entry.
170+
} else {
171+
diags.diagnose(suppressed.getLoc(),
172+
diag::suppress_not_protocol,
173+
suppressedTy);
174+
// FIXME: a great fix-it would be to remove the entire entry.
175+
}
126176
}
127177
}
128178

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol UserDefinedProtocol {}
4+
5+
class Who: ~SomebodyNew {} // expected-error {{cannot find type 'SomebodyNew' in scope}}
6+
7+
class Blah: ~UserDefinedProtocol {} // expected-error {{cannot suppress conformance on class}}
8+
9+
protocol Brotocol<T>: ~Copyable { // expected-error {{cannot suppress conformances here}}
10+
associatedtype T: ~Copyable // expected-error {{cannot suppress conformances here}}
11+
}
12+
13+
func inspectGPTCoin<T: ~Copyable>(_ t: T) {}
14+
// expected-error@-1 {{expected a class type or protocol-constrained type restricting 'T'}}
15+
16+
// FIXME: not a pleasant set of errors!
17+
func piano<T>(_ t: T) where T: ~Copyable {}
18+
// expected-error@-1 {{expected '{' in body of function declaration}}
19+
// expected-error@-2 {{consecutive statements on a line must be separated by ';'}}
20+
// expected-error@-3 {{expected type}}
21+
// expected-error@-4 {{'any Copyable' cannot be constructed because it has no accessible initializers}}
22+
23+
struct DoubleNotCopyable: ~Copyable, // expected-note {{'Copyable' previously suppressed here}}
24+
Sendable,
25+
~Copyable { } // expected-error {{implicit conformance to 'Copyable' already suppressed}}
26+
27+
extension DoubleNotCopyable: ~Copyable {} // expected-error {{cannot suppress conformances here}}
28+
29+
enum Colors: ~Equatable, ~Hashable {
30+
// expected-error@-1 {{cannot suppress implicit conformance to 'Equatable'}}
31+
// expected-error@-2 {{cannot suppress implicit conformance to 'Hashable'}}
32+
case red
33+
case white
34+
case blue
35+
}
36+
37+
// FIXME: this is confusing! We need to require parens!!
38+
struct Composition: ~Copyable & Equatable {} // expected-error {{cannot suppress implicit conformance to 'Copyable & Equatable'}}
39+
40+
enum Whatever<T> : ~Copyable
41+
where T: ~Copyable {} // expected-error {{expected type}}
42+
// expected-error@-1 {{expected '{' in enum}}
43+
44+
struct SuppressANonProtocolType: ~Colors {} // expected-error {{suppression of non-protocol type 'Colors'}}

0 commit comments

Comments
 (0)