Skip to content

Commit 708586b

Browse files
committed
Sema: Remove a usage of hasInverseMarking()
This addresses some duplicate diagnostics from the previous commit.
1 parent 41df661 commit 708586b

File tree

3 files changed

+68
-113
lines changed

3 files changed

+68
-113
lines changed

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 65 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
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

2524
using 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

lib/Sema/TypeCheckInvertible.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ class StorageVisitor {
3232
};
3333

3434
/// Checks that all stored properties or associated values are Copyable.
35-
bool checkCopyableConformance(DeclContext *dc,
35+
void checkCopyableConformance(DeclContext *dc,
3636
ProtocolConformance *conformance);
3737

3838
/// Checks that all stored properties or associated values are Escapable.
39-
bool checkEscapableConformance(DeclContext *dc,
39+
void checkEscapableConformance(DeclContext *dc,
4040
ProtocolConformance *conformance);
4141
}
4242

test/Generics/inverse_generics.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,13 @@ struct Silly: ~Copyable, Copyable {} // expected-error {{struct 'Silly' required
166166
enum Sally: Copyable, ~Copyable, NeedsCopyable {} // expected-error {{enum 'Sally' required to be 'Copyable' but is marked with '~Copyable'}}
167167

168168
class NiceTry: ~Copyable, Copyable {} // expected-error {{classes cannot be '~Copyable'}}
169-
// expected-error@-1 {{class 'NiceTry' required to be 'Copyable' but is marked with '~Copyable}}
170169

171170
@_moveOnly class NiceTry2: Copyable {} // expected-error {{'@_moveOnly' attribute is only valid on structs or enums}}
172-
// expected-error@-1 {{class 'NiceTry2' required to be 'Copyable' but is marked with '~Copyable'}}
173171

174172

175173
struct Extendo: ~Copyable {}
176174
extension Extendo: Copyable, ~Copyable {} // expected-error {{cannot apply inverse '~Copyable' to extension}}
175+
// expected-error@-1 {{struct 'Extendo' required to be 'Copyable' but is marked with '~Copyable'}}
177176

178177
enum EnumExtendo {}
179178
extension EnumExtendo: ~Copyable {} // expected-error {{cannot apply inverse '~Copyable' to extension}}

0 commit comments

Comments
 (0)