Skip to content

Commit 6ea2034

Browse files
authored
[NFC] Refactor DerivedConformances code to reduce code duplication for diagnostics (#32884)
1 parent 12b6b75 commit 6ea2034

File tree

4 files changed

+121
-112
lines changed

4 files changed

+121
-112
lines changed

lib/Sema/DerivedConformanceComparable.cpp

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -343,28 +343,10 @@ void DerivedConformance::tryDiagnoseFailedComparableDerivation(
343343
DeclContext *DC, NominalTypeDecl *nominal) {
344344
auto &ctx = DC->getASTContext();
345345
auto *comparableProto = ctx.getProtocol(KnownProtocolKind::Comparable);
346-
if (!isa<EnumDecl>(nominal)) {
347-
auto contextDecl = DC->getAsDecl();
348-
ctx.Diags.diagnose(
349-
contextDecl->getLoc(), diag::automatic_protocol_synthesis_unsupported,
350-
comparableProto->getName().str(), isa<StructDecl>(contextDecl));
351-
}
352-
353-
if (auto *enumDecl = dyn_cast<EnumDecl>(nominal)) {
354-
auto nonconformingAssociatedTypes =
355-
DerivedConformance::associatedValuesNotConformingToProtocol(
356-
DC, enumDecl, comparableProto);
357-
for (auto *typeToDiagnose : nonconformingAssociatedTypes) {
358-
SourceLoc reprLoc;
359-
if (auto *repr = typeToDiagnose->getTypeRepr())
360-
reprLoc = repr->getStartLoc();
361-
ctx.Diags.diagnose(
362-
reprLoc, diag::missing_member_type_conformance_prevents_synthesis, 0,
363-
typeToDiagnose->getInterfaceType(),
364-
comparableProto->getDeclaredType(),
365-
nominal->getDeclaredInterfaceType());
366-
}
346+
diagnoseAnyNonConformingMemberTypes(DC, nominal, comparableProto);
347+
diagnoseIfSynthesisUnsupportedForDecl(nominal, comparableProto);
367348

349+
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
368350
if (enumDecl->hasRawType() && !enumDecl->getRawType()->is<ErrorType>()) {
369351
auto rawType = enumDecl->getRawType();
370352
auto rawTypeLoc = enumDecl->getInherited()[0].getSourceRange().Start;

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 9 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -31,49 +31,6 @@
3131

3232
using namespace swift;
3333

34-
enum NonconformingMemberKind {
35-
AssociatedValue,
36-
StoredProperty
37-
};
38-
39-
/// Returns the VarDecl of each stored property in the given struct whose type
40-
/// does not conform to a protocol.
41-
/// \p theStruct The struct whose stored properties should be checked.
42-
/// \p protocol The protocol being requested.
43-
/// \return The VarDecl of each stored property whose type does not conform.
44-
static SmallVector<VarDecl *, 3>
45-
storedPropertiesNotConformingToProtocol(DeclContext *DC, StructDecl *theStruct,
46-
ProtocolDecl *protocol) {
47-
auto storedProperties = theStruct->getStoredProperties();
48-
SmallVector<VarDecl *, 3> nonconformingProperties;
49-
for (auto propertyDecl : storedProperties) {
50-
if (!propertyDecl->isUserAccessible())
51-
continue;
52-
53-
auto type = propertyDecl->getValueInterfaceType();
54-
if (!type)
55-
nonconformingProperties.push_back(propertyDecl);
56-
57-
if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), protocol,
58-
DC)) {
59-
nonconformingProperties.push_back(propertyDecl);
60-
}
61-
}
62-
return nonconformingProperties;
63-
}
64-
65-
/// Returns true if every stored property in the given struct conforms to the
66-
/// protocol (or, vacuously, if it has no stored properties).
67-
/// \p theStruct The struct whose stored properties should be checked.
68-
/// \p protocol The protocol being requested.
69-
/// \return True if all stored properties of the struct conform.
70-
static bool allStoredPropertiesConformToProtocol(DeclContext *DC,
71-
StructDecl *theStruct,
72-
ProtocolDecl *protocol) {
73-
return storedPropertiesNotConformingToProtocol(DC, theStruct, protocol)
74-
.empty();
75-
}
76-
7734
/// Common preconditions for Equatable and Hashable.
7835
static bool canDeriveConformance(DeclContext *DC,
7936
NominalTypeDecl *target,
@@ -86,55 +43,16 @@ static bool canDeriveConformance(DeclContext *DC,
8643
}
8744

8845
if (auto structDecl = dyn_cast<StructDecl>(target)) {
89-
// All stored properties of the struct must conform to the protocol.
90-
return allStoredPropertiesConformToProtocol(DC, structDecl, protocol);
46+
// All stored properties of the struct must conform to the protocol. If
47+
// there are no stored properties, we will vaccously return true.
48+
return DerivedConformance::storedPropertiesNotConformingToProtocol(
49+
DC, structDecl, protocol)
50+
.empty();
9151
}
9252

9353
return false;
9454
}
9555

96-
/// Diagnose failed conformance synthesis caused by a member type not conforming
97-
/// to the same protocol
98-
void diagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal,
99-
ProtocolDecl *protocol) {
100-
ASTContext &ctx = DC->getASTContext();
101-
102-
if (auto *enumDecl = dyn_cast<EnumDecl>(nominal)) {
103-
auto nonconformingAssociatedTypes =
104-
DerivedConformance::associatedValuesNotConformingToProtocol(DC, enumDecl, protocol);
105-
for (auto *typeToDiagnose : nonconformingAssociatedTypes) {
106-
SourceLoc reprLoc;
107-
if (auto *repr = typeToDiagnose->getTypeRepr())
108-
reprLoc = repr->getStartLoc();
109-
ctx.Diags.diagnose(
110-
reprLoc,
111-
diag::missing_member_type_conformance_prevents_synthesis,
112-
NonconformingMemberKind::AssociatedValue,
113-
typeToDiagnose->getInterfaceType(), protocol->getDeclaredType(),
114-
nominal->getDeclaredInterfaceType());
115-
}
116-
}
117-
118-
if (auto *structDecl = dyn_cast<StructDecl>(nominal)) {
119-
auto nonconformingStoredProperties =
120-
storedPropertiesNotConformingToProtocol(DC, structDecl, protocol);
121-
for (auto *propertyToDiagnose : nonconformingStoredProperties) {
122-
ctx.Diags.diagnose(
123-
propertyToDiagnose->getLoc(),
124-
diag::missing_member_type_conformance_prevents_synthesis,
125-
NonconformingMemberKind::StoredProperty,
126-
propertyToDiagnose->getInterfaceType(), protocol->getDeclaredType(),
127-
nominal->getDeclaredInterfaceType());
128-
}
129-
}
130-
131-
if (auto *classDecl = dyn_cast<ClassDecl>(nominal)) {
132-
ctx.Diags.diagnose(classDecl->getLoc(),
133-
diag::automatic_protocol_synthesis_unsupported,
134-
protocol->getName().str(), 0);
135-
}
136-
}
137-
13856
static std::pair<BraceStmt *, bool>
13957
deriveBodyEquatable_enum_uninhabited_eq(AbstractFunctionDecl *eqDecl, void *) {
14058
auto parentDC = eqDecl->getDeclContext();
@@ -565,7 +483,8 @@ void DerivedConformance::tryDiagnoseFailedEquatableDerivation(
565483
DeclContext *DC, NominalTypeDecl *nominal) {
566484
ASTContext &ctx = DC->getASTContext();
567485
auto *equatableProto = ctx.getProtocol(KnownProtocolKind::Equatable);
568-
diagnoseFailedDerivation(DC, nominal, equatableProto);
486+
diagnoseAnyNonConformingMemberTypes(DC, nominal, equatableProto);
487+
diagnoseIfSynthesisUnsupportedForDecl(nominal, equatableProto);
569488
}
570489

571490
/// Returns a new \c CallExpr representing
@@ -1048,7 +967,8 @@ void DerivedConformance::tryDiagnoseFailedHashableDerivation(
1048967
DeclContext *DC, NominalTypeDecl *nominal) {
1049968
ASTContext &ctx = DC->getASTContext();
1050969
auto *hashableProto = ctx.getProtocol(KnownProtocolKind::Hashable);
1051-
diagnoseFailedDerivation(DC, nominal, hashableProto);
970+
diagnoseAnyNonConformingMemberTypes(DC, nominal, hashableProto);
971+
diagnoseIfSynthesisUnsupportedForDecl(nominal, hashableProto);
1052972
}
1053973

1054974
ValueDecl *DerivedConformance::deriveHashable(ValueDecl *requirement) {

lib/Sema/DerivedConformances.cpp

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
using namespace swift;
2626

27+
enum NonconformingMemberKind { AssociatedValue, StoredProperty };
28+
2729
DerivedConformance::DerivedConformance(ASTContext &ctx, Decl *conformanceDecl,
2830
NominalTypeDecl *nominal,
2931
ProtocolDecl *protocol)
@@ -159,15 +161,34 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
159161
return false;
160162
}
161163

164+
SmallVector<VarDecl *, 3>
165+
DerivedConformance::storedPropertiesNotConformingToProtocol(
166+
DeclContext *DC, StructDecl *theStruct, ProtocolDecl *protocol) {
167+
auto storedProperties = theStruct->getStoredProperties();
168+
SmallVector<VarDecl *, 3> nonconformingProperties;
169+
for (auto propertyDecl : storedProperties) {
170+
if (!propertyDecl->isUserAccessible())
171+
continue;
172+
173+
auto type = propertyDecl->getValueInterfaceType();
174+
if (!type)
175+
nonconformingProperties.push_back(propertyDecl);
176+
177+
if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), protocol,
178+
DC)) {
179+
nonconformingProperties.push_back(propertyDecl);
180+
}
181+
}
182+
return nonconformingProperties;
183+
}
184+
162185
void DerivedConformance::tryDiagnoseFailedDerivation(DeclContext *DC,
163186
NominalTypeDecl *nominal,
164187
ProtocolDecl *protocol) {
165188
auto knownProtocol = protocol->getKnownProtocolKind();
166189
if (!knownProtocol)
167190
return;
168191

169-
// Comparable on eligible type kinds should never fail
170-
171192
if (*knownProtocol == KnownProtocolKind::Equatable) {
172193
tryDiagnoseFailedEquatableDerivation(DC, nominal);
173194
}
@@ -181,6 +202,60 @@ void DerivedConformance::tryDiagnoseFailedDerivation(DeclContext *DC,
181202
}
182203
}
183204

205+
void DerivedConformance::diagnoseAnyNonConformingMemberTypes(
206+
DeclContext *DC, NominalTypeDecl *nominal, ProtocolDecl *protocol) {
207+
ASTContext &ctx = DC->getASTContext();
208+
209+
if (auto *enumDecl = dyn_cast<EnumDecl>(nominal)) {
210+
auto nonconformingAssociatedTypes =
211+
associatedValuesNotConformingToProtocol(DC, enumDecl, protocol);
212+
for (auto *typeToDiagnose : nonconformingAssociatedTypes) {
213+
SourceLoc reprLoc;
214+
if (auto *repr = typeToDiagnose->getTypeRepr())
215+
reprLoc = repr->getStartLoc();
216+
ctx.Diags.diagnose(
217+
reprLoc, diag::missing_member_type_conformance_prevents_synthesis,
218+
NonconformingMemberKind::AssociatedValue,
219+
typeToDiagnose->getInterfaceType(), protocol->getDeclaredType(),
220+
nominal->getDeclaredInterfaceType());
221+
}
222+
}
223+
224+
if (auto *structDecl = dyn_cast<StructDecl>(nominal)) {
225+
auto nonconformingStoredProperties =
226+
storedPropertiesNotConformingToProtocol(DC, structDecl, protocol);
227+
for (auto *propertyToDiagnose : nonconformingStoredProperties) {
228+
ctx.Diags.diagnose(
229+
propertyToDiagnose->getLoc(),
230+
diag::missing_member_type_conformance_prevents_synthesis,
231+
NonconformingMemberKind::StoredProperty,
232+
propertyToDiagnose->getInterfaceType(), protocol->getDeclaredType(),
233+
nominal->getDeclaredInterfaceType());
234+
}
235+
}
236+
}
237+
238+
void DerivedConformance::diagnoseIfSynthesisUnsupportedForDecl(
239+
NominalTypeDecl *nominal, ProtocolDecl *protocol) {
240+
auto shouldDiagnose = false;
241+
242+
if (protocol->isSpecificProtocol(KnownProtocolKind::Equatable) ||
243+
protocol->isSpecificProtocol(KnownProtocolKind::Hashable)) {
244+
shouldDiagnose = isa<ClassDecl>(nominal);
245+
}
246+
247+
if (protocol->isSpecificProtocol(KnownProtocolKind::Comparable)) {
248+
shouldDiagnose = !isa<EnumDecl>(nominal);
249+
}
250+
251+
if (shouldDiagnose) {
252+
auto &ctx = nominal->getASTContext();
253+
ctx.Diags.diagnose(nominal->getLoc(),
254+
diag::automatic_protocol_synthesis_unsupported,
255+
protocol->getName().str(), isa<StructDecl>(nominal));
256+
}
257+
}
258+
184259
ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
185260
ValueDecl *requirement) {
186261
// Note: whenever you update this function, also update

lib/Sema/DerivedConformances.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ class DerivedConformance {
5050
/// Get the declared type of the protocol that this is conformance is for.
5151
Type getProtocolType() const;
5252

53+
/// Returns the VarDecl of each stored property in the given struct whose type
54+
/// does not conform to a protocol.
55+
/// \p theStruct The struct whose stored properties should be checked.
56+
/// \p protocol The protocol being requested.
57+
/// \return The VarDecl of each stored property whose type does not conform.
58+
static SmallVector<VarDecl *, 3> storedPropertiesNotConformingToProtocol(
59+
DeclContext *DC, StructDecl *theStruct, ProtocolDecl *protocol);
60+
5361
/// True if the type can implicitly derive a conformance for the given
5462
/// protocol.
5563
///
@@ -80,6 +88,30 @@ class DerivedConformance {
8088
NominalTypeDecl *nominal,
8189
ProtocolDecl *protocol);
8290

91+
/// Diagnose any members which do not conform to the protocol for which
92+
/// we were trying to synthesize the conformance to.
93+
///
94+
/// \param nominal The nominal type for which we would like to diagnose
95+
/// derivation failures
96+
///
97+
/// \param protocol The protocol with requirements we would like to diagnose
98+
/// derivation failures for
99+
static void diagnoseAnyNonConformingMemberTypes(DeclContext *DC,
100+
NominalTypeDecl *nominal,
101+
ProtocolDecl *protocol);
102+
103+
/// Diagnose the declaration for which we were trying to synthesize
104+
/// the conformance for, if the synthesis is not supported for that
105+
/// declaration.
106+
///
107+
/// \param nominal The nominal type for which we would like to diagnose
108+
/// derivation failures
109+
///
110+
/// \param protocol The protocol with requirements we would like to diagnose
111+
/// derivation failures for
112+
static void diagnoseIfSynthesisUnsupportedForDecl(NominalTypeDecl *nominal,
113+
ProtocolDecl *protocol);
114+
83115
/// Determine the derivable requirement that would satisfy the given
84116
/// requirement, if there is one.
85117
///

0 commit comments

Comments
 (0)