19
19
#include " TypeCheckInvertible.h"
20
20
#include " swift/AST/ASTContext.h"
21
21
#include " swift/AST/GenericEnvironment.h"
22
- #include " swift/AST/InverseMarking.h"
23
22
#include " TypeChecker.h"
24
23
25
24
using namespace swift ;
@@ -50,63 +49,34 @@ static void addConformanceFixIt(const NominalTypeDecl *nominal,
50
49
// If there is not already an inverse ~KP applied to this type, suggest it.
51
50
// The goal here is that we want to tell users how they can suppress or remove
52
51
// a conformance to KP.
53
- static void emitAdviceToApplyInverseAfter (InFlightDiagnostic &&diag,
54
- InvertibleProtocolKind ip,
55
- InverseMarking::Mark inverseMarking,
52
+ static void emitAdviceToApplyInverseAfter (InvertibleProtocolKind ip,
53
+ bool canAddInverse,
56
54
NominalTypeDecl *nominal) {
57
55
auto kp = getKnownProtocolKind (ip);
58
56
59
- // Immediately flush, then emit notes, so they're associated.
60
- diag.flush ();
61
-
62
- // Have no advice for situations where the KP conformance is explicit.
63
- InvertibleProtocolSet inverses;
64
- bool anyObject = false ;
65
- auto inheritedNominals = getDirectlyInheritedNominalTypeDecls (
66
- nominal, inverses, anyObject);
67
- for (auto entry : inheritedNominals) {
68
- if (auto *otherProto = dyn_cast<ProtocolDecl>(entry.Item )) {
69
- if (otherProto->isSpecificProtocol (kp))
70
- return ;
71
- }
72
- }
73
-
74
- switch (inverseMarking.getKind ()) {
75
- case InverseMarking::Kind::None: {
76
- // Suggest adding ~KP to make it non-KP.
57
+ if (canAddInverse) {
77
58
auto diag = nominal->diagnose (diag::add_inverse,
78
- nominal,
79
- getProtocolName (kp));
59
+ nominal,
60
+ getProtocolName (kp));
80
61
addConformanceFixIt (nominal, diag, kp, /* inverse=*/ true );
81
62
}
82
- break ;
83
- case InverseMarking::Kind::LegacyExplicit:
84
- case InverseMarking::Kind::Explicit:
85
- // FIXME: we can probably do better here. Look for the extension where the
86
- // inverse came from.
87
- break ;
88
- }
89
63
}
90
64
91
65
// / Emit fix-it's to help the user resolve a containment issue where the
92
66
// / \c nonConformingTy needs to be made to conform to \c kp to resolve a
93
67
// / containment issue.
94
68
// / \param enclosingNom is the nominal type containing a nonconforming value
95
69
// / \param nonConformingTy is the type of the nonconforming value
96
- static void tryEmitContainmentFixits (InFlightDiagnostic &&diag ,
97
- NominalTypeDecl *enclosingNom ,
70
+ static void tryEmitContainmentFixits (NominalTypeDecl *enclosingNom ,
71
+ bool canAddInverse ,
98
72
Type nonConformingTy,
99
73
InvertibleProtocolKind ip) {
100
74
auto *module = enclosingNom->getParentModule ();
101
75
auto &ctx = enclosingNom->getASTContext ();
102
76
auto kp = getKnownProtocolKind (ip);
103
77
104
- // Check the enclosing type's markings to see what to suggest.
105
- auto enclosingMarking = enclosingNom->hasInverseMarking (ip);
106
-
107
78
// First, the generic advice.
108
- emitAdviceToApplyInverseAfter (std::move (diag), ip,
109
- enclosingMarking, enclosingNom);
79
+ emitAdviceToApplyInverseAfter (ip, canAddInverse, enclosingNom);
110
80
111
81
// If it's a generic parameter defined in the same module, point to the
112
82
// parameter that must have had the inverse applied to it somewhere.
@@ -129,90 +99,84 @@ static void tryEmitContainmentFixits(InFlightDiagnostic &&diag,
129
99
// not IP.
130
100
if (auto nominal = nonConformingTy->getAnyNominal ()) {
131
101
if (nominal->getLoc (/* SerializedOK=*/ false )) {
132
- auto inverse = nominal->hasInverseMarking (ip);
133
- auto loc = inverse.getLoc ();
134
-
135
- switch (inverse.getKind ()) {
136
- case InverseMarking::Kind::None:
137
- assert (false && " how did it become noncopyable/nonescapable then?" );
138
- break ;
139
- case InverseMarking::Kind::LegacyExplicit:
140
- case InverseMarking::Kind::Explicit:
141
- assert (loc);
142
- ctx.Diags .diagnose (loc,
143
- diag::note_inverse_preventing_conformance_explicit,
144
- nominal, getProtocolName (kp));
145
- break ;
146
- }
102
+ ctx.Diags .diagnose (nominal->getLoc (),
103
+ diag::note_inverse_preventing_conformance_explicit,
104
+ nominal, getProtocolName (kp));
147
105
}
148
106
}
149
107
}
150
108
151
109
// / MARK: conformance checking
152
- static bool checkInvertibleConformanceCommon (DeclContext *dc,
110
+ static void checkInvertibleConformanceCommon (DeclContext *dc,
153
111
ProtocolConformance *conformance,
154
112
InvertibleProtocolKind ip) {
155
113
const auto kp = getKnownProtocolKind (ip);
156
114
auto *proto = conformance->getProtocol ();
157
115
assert (proto->isSpecificProtocol (kp));
158
116
159
- auto *nom = conformance-> getType ()-> getAnyNominal ();
160
- assert (nom && " non-nominal with conformance? " );
161
- if (!nom)
162
- return false ;
117
+ auto *nominalDecl = dc-> getSelfNominalTypeDecl ();
118
+ assert (isa<StructDecl>(nominalDecl) ||
119
+ isa<EnumDecl>(nominalDecl) ||
120
+ isa<ClassDecl>(nominalDecl)) ;
163
121
164
- auto &ctx = nom->getASTContext ();
165
- bool conforms = true ;
166
-
167
- // An explicit `~IP` prevents conformance if it appears on the same
168
- // declaration that also declares the conformance.
169
- //
170
- // So, if the nominal has `~Copyable` but this conformance is
171
- // written in an extension, then we do not raise an error.
172
- auto inverseMarking = nom->hasInverseMarking (ip);
173
- if (inverseMarking.isAnyExplicit ()) {
174
- if (dc == nom) {
175
- ctx.Diags .diagnose (inverseMarking.getLoc (),
122
+ auto &ctx = nominalDecl->getASTContext ();
123
+
124
+ InvertibleProtocolSet inverses;
125
+ bool anyObject = false ;
126
+ (void ) getDirectlyInheritedNominalTypeDecls (nominalDecl, inverses, anyObject);
127
+
128
+ // Handle deprecated attributes.
129
+ if (nominalDecl->getAttrs ().hasAttribute <MoveOnlyAttr>())
130
+ inverses.insert (InvertibleProtocolKind::Copyable);
131
+ if (nominalDecl->getAttrs ().hasAttribute <NonEscapableAttr>())
132
+ inverses.insert (InvertibleProtocolKind::Escapable);
133
+
134
+ bool hasExplicitInverse = inverses.contains (ip);
135
+
136
+ bool hasUnconditionalConformance = false ;
137
+ auto *normalConf = dyn_cast<NormalProtocolConformance>(conformance);
138
+ if (normalConf && normalConf->getConditionalRequirements ().empty ())
139
+ hasUnconditionalConformance = true ;
140
+
141
+ if (!isa<ClassDecl>(nominalDecl) ||
142
+ ctx.LangOpts .hasFeature (Feature::MoveOnlyClasses)) {
143
+ // If the inheritance clause contains ~Copyable, reject an unconditional
144
+ // conformance to Copyable.
145
+ if (hasExplicitInverse && hasUnconditionalConformance) {
146
+ ctx.Diags .diagnose (normalConf->getLoc (),
176
147
diag::inverse_but_also_conforms,
177
- nom, getProtocolName (kp));
178
- conforms &= false ;
148
+ nominalDecl, getProtocolName (kp));
179
149
}
180
150
}
181
151
182
152
// All classes can store noncopyable/nonescaping values.
183
- if (isa<ClassDecl>(nom ))
184
- return conforms ;
153
+ if (isa<ClassDecl>(nominalDecl ))
154
+ return ;
185
155
186
- // Protocols do not directly define any storage.
187
- if (isa<ProtocolDecl, BuiltinTupleDecl>(nom))
188
- llvm_unreachable (" unexpected nominal to check invertible's conformance" );
156
+ bool canAddInverse = !hasExplicitInverse && !hasUnconditionalConformance;
189
157
190
158
// A deinit prevents a struct or enum from conforming to Copyable.
191
159
if (ip == InvertibleProtocolKind::Copyable) {
192
- if (auto *deinit = nom->getValueTypeDestructor ()) {
193
- auto diag = deinit->diagnose (diag::copyable_illegal_deinit, nom);
194
- emitAdviceToApplyInverseAfter (std::move (diag),
195
- ip,
196
- inverseMarking,
197
- nom);
198
- conforms &= false ;
160
+ if (auto *deinit = nominalDecl->getValueTypeDestructor ()) {
161
+ deinit->diagnose (diag::copyable_illegal_deinit, nominalDecl);
162
+ emitAdviceToApplyInverseAfter (ip, canAddInverse, nominalDecl);
199
163
}
200
164
}
201
165
202
- // Otherwise, we have to check its storage to ensure it is all
203
- // Copyable/Escapable.
166
+ // Check storage for conformance to Copyable/Escapable.
204
167
205
168
class LacksMatchingStorage : public StorageVisitor {
206
169
NominalTypeDecl *Nominal;
207
170
DeclContext *DC;
208
171
InvertibleProtocolKind IP;
209
- bool Diagnosing ;
172
+ bool CanAddInverse ;
210
173
public:
211
174
LacksMatchingStorage (NominalTypeDecl *nom,
212
175
DeclContext *dc,
213
- InvertibleProtocolKind ip,
214
- bool diagnose)
215
- : Nominal(nom), DC(dc), IP(ip), Diagnosing(diagnose) {}
176
+ bool canAddInverse,
177
+ InvertibleProtocolKind ip)
178
+ : Nominal(nom), DC(dc), IP(ip),
179
+ CanAddInverse (canAddInverse) {}
216
180
217
181
bool visit () { return StorageVisitor::visit (Nominal, DC); }
218
182
@@ -233,15 +197,11 @@ static bool checkInvertibleConformanceCommon(DeclContext *dc,
233
197
break ;
234
198
}
235
199
236
- if (!Diagnosing)
237
- return true ; // it's got storage missing conformance to IP
200
+ storage->diagnose (diag::inverse_type_member_in_conforming_type,
201
+ type, isEnum, storage->getName (), Nominal,
202
+ getProtocolName (getKnownProtocolKind (IP)));
238
203
239
- auto diag =
240
- storage->diagnose (diag::inverse_type_member_in_conforming_type,
241
- type, isEnum, storage->getName (), Nominal,
242
- getProtocolName (getKnownProtocolKind (IP)));
243
-
244
- tryEmitContainmentFixits (std::move (diag), Nominal, type, IP);
204
+ tryEmitContainmentFixits (Nominal, CanAddInverse, type, IP);
245
205
return true ;
246
206
}
247
207
@@ -260,23 +220,19 @@ static bool checkInvertibleConformanceCommon(DeclContext *dc,
260
220
261
221
// This nominal cannot conform to IP if it contains storage that does not
262
222
// conform to IP.
263
- bool lacksMatchingStorage =
264
- LacksMatchingStorage (nom, dc, ip, /* diagnose=*/ true ).visit ();
265
- conforms &= !lacksMatchingStorage;
266
-
267
- return conforms;
223
+ LacksMatchingStorage (nominalDecl, dc, canAddInverse, ip).visit();
268
224
}
269
225
270
- bool swift::checkEscapableConformance (DeclContext *dc,
226
+ void swift::checkEscapableConformance (DeclContext *dc,
271
227
ProtocolConformance *conformance) {
272
- return checkInvertibleConformanceCommon (dc, conformance,
273
- InvertibleProtocolKind::Escapable);
228
+ checkInvertibleConformanceCommon (dc, conformance,
229
+ InvertibleProtocolKind::Escapable);
274
230
}
275
231
276
- bool swift::checkCopyableConformance (DeclContext *dc,
232
+ void swift::checkCopyableConformance (DeclContext *dc,
277
233
ProtocolConformance *conformance) {
278
- return checkInvertibleConformanceCommon (dc, conformance,
279
- InvertibleProtocolKind::Copyable);
234
+ checkInvertibleConformanceCommon (dc, conformance,
235
+ InvertibleProtocolKind::Copyable);
280
236
}
281
237
282
238
// / Visit the instance storage of the given nominal type as seen through
0 commit comments