1919#include " TypeCheckInvertible.h"
2020#include " swift/AST/ASTContext.h"
2121#include " swift/AST/GenericEnvironment.h"
22- #include " swift/AST/InverseMarking.h"
2322#include " TypeChecker.h"
2423
2524using namespace swift ;
@@ -50,63 +49,34 @@ static void addConformanceFixIt(const NominalTypeDecl *nominal,
5049// If there is not already an inverse ~KP applied to this type, suggest it.
5150// The goal here is that we want to tell users how they can suppress or remove
5251// 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,
5654 NominalTypeDecl *nominal) {
5755 auto kp = getKnownProtocolKind (ip);
5856
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) {
7758 auto diag = nominal->diagnose (diag::add_inverse,
78- nominal,
79- getProtocolName (kp));
59+ nominal,
60+ getProtocolName (kp));
8061 addConformanceFixIt (nominal, diag, kp, /* inverse=*/ true );
8162 }
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- }
8963}
9064
9165// / Emit fix-it's to help the user resolve a containment issue where the
9266// / \c nonConformingTy needs to be made to conform to \c kp to resolve a
9367// / containment issue.
9468// / \param enclosingNom is the nominal type containing a nonconforming value
9569// / \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 ,
9872 Type nonConformingTy,
9973 InvertibleProtocolKind ip) {
10074 auto *module = enclosingNom->getParentModule ();
10175 auto &ctx = enclosingNom->getASTContext ();
10276 auto kp = getKnownProtocolKind (ip);
10377
104- // Check the enclosing type's markings to see what to suggest.
105- auto enclosingMarking = enclosingNom->hasInverseMarking (ip);
106-
10778 // First, the generic advice.
108- emitAdviceToApplyInverseAfter (std::move (diag), ip,
109- enclosingMarking, enclosingNom);
79+ emitAdviceToApplyInverseAfter (ip, canAddInverse, enclosingNom);
11080
11181 // If it's a generic parameter defined in the same module, point to the
11282 // parameter that must have had the inverse applied to it somewhere.
@@ -129,90 +99,84 @@ static void tryEmitContainmentFixits(InFlightDiagnostic &&diag,
12999 // not IP.
130100 if (auto nominal = nonConformingTy->getAnyNominal ()) {
131101 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));
147105 }
148106 }
149107}
150108
151109// / MARK: conformance checking
152- static bool checkInvertibleConformanceCommon (DeclContext *dc,
110+ static void checkInvertibleConformanceCommon (DeclContext *dc,
153111 ProtocolConformance *conformance,
154112 InvertibleProtocolKind ip) {
155113 const auto kp = getKnownProtocolKind (ip);
156114 auto *proto = conformance->getProtocol ();
157115 assert (proto->isSpecificProtocol (kp));
158116
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)) ;
163121
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 (),
176147 diag::inverse_but_also_conforms,
177- nom, getProtocolName (kp));
178- conforms &= false ;
148+ nominalDecl, getProtocolName (kp));
179149 }
180150 }
181151
182152 // All classes can store noncopyable/nonescaping values.
183- if (isa<ClassDecl>(nom ))
184- return conforms ;
153+ if (isa<ClassDecl>(nominalDecl ))
154+ return ;
185155
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;
189157
190158 // A deinit prevents a struct or enum from conforming to Copyable.
191159 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);
199163 }
200164 }
201165
202- // Otherwise, we have to check its storage to ensure it is all
203- // Copyable/Escapable.
166+ // Check storage for conformance to Copyable/Escapable.
204167
205168 class LacksMatchingStorage : public StorageVisitor {
206169 NominalTypeDecl *Nominal;
207170 DeclContext *DC;
208171 InvertibleProtocolKind IP;
209- bool Diagnosing ;
172+ bool CanAddInverse ;
210173 public:
211174 LacksMatchingStorage (NominalTypeDecl *nom,
212175 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) {}
216180
217181 bool visit () { return StorageVisitor::visit (Nominal, DC); }
218182
@@ -233,15 +197,11 @@ static bool checkInvertibleConformanceCommon(DeclContext *dc,
233197 break ;
234198 }
235199
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)));
238203
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);
245205 return true ;
246206 }
247207
@@ -260,23 +220,19 @@ static bool checkInvertibleConformanceCommon(DeclContext *dc,
260220
261221 // This nominal cannot conform to IP if it contains storage that does not
262222 // 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();
268224}
269225
270- bool swift::checkEscapableConformance (DeclContext *dc,
226+ void swift::checkEscapableConformance (DeclContext *dc,
271227 ProtocolConformance *conformance) {
272- return checkInvertibleConformanceCommon (dc, conformance,
273- InvertibleProtocolKind::Escapable);
228+ checkInvertibleConformanceCommon (dc, conformance,
229+ InvertibleProtocolKind::Escapable);
274230}
275231
276- bool swift::checkCopyableConformance (DeclContext *dc,
232+ void swift::checkCopyableConformance (DeclContext *dc,
277233 ProtocolConformance *conformance) {
278- return checkInvertibleConformanceCommon (dc, conformance,
279- InvertibleProtocolKind::Copyable);
234+ checkInvertibleConformanceCommon (dc, conformance,
235+ InvertibleProtocolKind::Copyable);
280236}
281237
282238// / Visit the instance storage of the given nominal type as seen through
0 commit comments