32
32
33
33
using namespace swift ;
34
34
35
- // / Returns true if, for every element of the given enum, it either has no
36
- // / associated values or all of them conform to a protocol.
35
+ enum NonconformingMemberKind {
36
+ AssociatedValue,
37
+ StoredProperty
38
+ };
39
+
40
+ // / Returns the ParamDecl for each associated value of the given enum whose type
41
+ // / does not conform to a protocol
37
42
// / \p theEnum The enum whose elements and associated values should be checked.
38
43
// / \p protocol The protocol being requested.
39
- // / \return True if all associated values of all elements of the enum conform.
40
- static bool allAssociatedValuesConformToProtocol (DeclContext *DC,
41
- EnumDecl *theEnum,
42
- ProtocolDecl *protocol) {
44
+ // / \return The ParamDecl of each associated value whose type does not conform.
45
+ static SmallVector<ParamDecl *, 3 >
46
+ associatedValuesNotConformingToProtocol (DeclContext *DC, EnumDecl *theEnum,
47
+ ProtocolDecl *protocol) {
43
48
auto lazyResolver = DC->getASTContext ().getLazyResolver ();
49
+ SmallVector<ParamDecl *, 3 > nonconformingAssociatedValues;
44
50
for (auto elt : theEnum->getAllElements ()) {
45
51
if (!elt->hasInterfaceType ())
46
52
lazyResolver->resolveDeclSignature (elt);
@@ -53,38 +59,62 @@ static bool allAssociatedValuesConformToProtocol(DeclContext *DC,
53
59
auto type = param->getInterfaceType ();
54
60
if (!TypeChecker::conformsToProtocol (DC->mapTypeIntoContext (type),
55
61
protocol, DC, None)) {
56
- return false ;
62
+ nonconformingAssociatedValues. push_back (param) ;
57
63
}
58
64
}
59
65
}
60
- return true ;
66
+ return nonconformingAssociatedValues ;
61
67
}
62
68
63
- // / Returns true if every stored property in the given struct conforms to the
64
- // / protocol (or, vacuously, if it has no stored properties) .
65
- // / \p theStruct The struct whose stored properties should be checked.
69
+ // / Returns true if, for every element of the given enum, it either has no
70
+ // / associated values or all of them conform to a protocol .
71
+ // / \p theEnum The enum whose elements and associated values should be checked.
66
72
// / \p protocol The protocol being requested.
67
- // / \return True if all stored properties of the struct conform.
68
- static bool allStoredPropertiesConformToProtocol (DeclContext *DC,
69
- StructDecl *theStruct ,
73
+ // / \return True if all associated values of all elements of the enum conform.
74
+ static bool allAssociatedValuesConformToProtocol (DeclContext *DC,
75
+ EnumDecl *theEnum ,
70
76
ProtocolDecl *protocol) {
77
+ return associatedValuesNotConformingToProtocol (DC, theEnum, protocol).empty ();
78
+ }
79
+
80
+ // / Returns the VarDecl of each stored property in the given struct whose type
81
+ // / does not conform to a protocol.
82
+ // / \p theStruct The struct whose stored properties should be checked.
83
+ // / \p protocol The protocol being requested.
84
+ // / \return The VarDecl of each stored property whose type does not conform.
85
+ static SmallVector<VarDecl *, 3 >
86
+ storedPropertiesNotConformingToProtocol (DeclContext *DC, StructDecl *theStruct,
87
+ ProtocolDecl *protocol) {
71
88
auto lazyResolver = DC->getASTContext ().getLazyResolver ();
72
89
auto storedProperties =
73
- theStruct->getStoredProperties (/* skipInaccessible=*/ true );
90
+ theStruct->getStoredProperties (/* skipInaccessible=*/ true );
91
+ SmallVector<VarDecl *, 3 > nonconformingProperties;
74
92
for (auto propertyDecl : storedProperties) {
75
93
if (!propertyDecl->hasInterfaceType ())
76
94
lazyResolver->resolveDeclSignature (propertyDecl);
77
95
if (!propertyDecl->hasInterfaceType ())
78
- return false ;
96
+ nonconformingProperties. push_back (propertyDecl) ;
79
97
80
98
auto type = propertyDecl->getValueInterfaceType ();
81
99
82
- if (!TypeChecker::conformsToProtocol (DC->mapTypeIntoContext (type),
83
- protocol, DC, None)) {
84
- return false ;
100
+ if (!TypeChecker::conformsToProtocol (DC->mapTypeIntoContext (type), protocol,
101
+ DC, None)) {
102
+ nonconformingProperties. push_back (propertyDecl) ;
85
103
}
86
104
}
87
- return true ;
105
+ return nonconformingProperties;
106
+ }
107
+
108
+ // / Returns true if every stored property in the given struct conforms to the
109
+ // / protocol (or, vacuously, if it has no stored properties).
110
+ // / \p theStruct The struct whose stored properties should be checked.
111
+ // / \p protocol The protocol being requested.
112
+ // / \return True if all stored properties of the struct conform.
113
+ static bool allStoredPropertiesConformToProtocol (DeclContext *DC,
114
+ StructDecl *theStruct,
115
+ ProtocolDecl *protocol) {
116
+ return storedPropertiesNotConformingToProtocol (DC, theStruct, protocol)
117
+ .empty ();
88
118
}
89
119
90
120
// / Common preconditions for Equatable and Hashable.
@@ -106,6 +136,39 @@ static bool canDeriveConformance(DeclContext *DC,
106
136
return false ;
107
137
}
108
138
139
+ // / Diagnose failed conformance synthesis caused by a member type not conforming
140
+ // / to the same protocol
141
+ void diagnoseFailedDerivation (DeclContext *DC, NominalTypeDecl *nominal,
142
+ ProtocolDecl *protocol) {
143
+ ASTContext &ctx = DC->getASTContext ();
144
+
145
+ if (auto *enumDecl = dyn_cast<EnumDecl>(nominal)) {
146
+ auto nonconformingAssociatedTypes =
147
+ associatedValuesNotConformingToProtocol (DC, enumDecl, protocol);
148
+ for (auto *typeToDiagnose : nonconformingAssociatedTypes) {
149
+ ctx.Diags .diagnose (
150
+ typeToDiagnose->getTypeLoc ().getLoc (),
151
+ diag::missing_member_type_conformance_prevents_synthesis,
152
+ NonconformingMemberKind::AssociatedValue,
153
+ typeToDiagnose->getInterfaceType (), protocol->getDeclaredType (),
154
+ nominal->getDeclaredInterfaceType ());
155
+ }
156
+ }
157
+
158
+ if (auto *structDecl = dyn_cast<StructDecl>(nominal)) {
159
+ auto nonconformingStoredProperties =
160
+ storedPropertiesNotConformingToProtocol (DC, structDecl, protocol);
161
+ for (auto *propertyToDiagnose : nonconformingStoredProperties) {
162
+ ctx.Diags .diagnose (
163
+ propertyToDiagnose->getLoc (),
164
+ diag::missing_member_type_conformance_prevents_synthesis,
165
+ NonconformingMemberKind::StoredProperty,
166
+ propertyToDiagnose->getInterfaceType (), protocol->getDeclaredType (),
167
+ nominal->getDeclaredInterfaceType ());
168
+ }
169
+ }
170
+ }
171
+
109
172
// / Creates a named variable based on a prefix character and a numeric index.
110
173
// / \p prefixChar The prefix character for the variable's name.
111
174
// / \p index The numeric index to append to the variable's name.
@@ -722,6 +785,13 @@ ValueDecl *DerivedConformance::deriveEquatable(ValueDecl *requirement) {
722
785
return nullptr ;
723
786
}
724
787
788
+ void DerivedConformance::tryDiagnoseFailedEquatableDerivation (
789
+ DeclContext *DC, NominalTypeDecl *nominal) {
790
+ ASTContext &ctx = DC->getASTContext ();
791
+ auto *equatableProto = ctx.getProtocol (KnownProtocolKind::Equatable);
792
+ diagnoseFailedDerivation (DC, nominal, equatableProto);
793
+ }
794
+
725
795
// / Returns a new \c CallExpr representing
726
796
// /
727
797
// / hasher.combine(hashable)
@@ -1055,18 +1125,41 @@ deriveBodyHashable_hashValue(AbstractFunctionDecl *hashValueDecl, void *) {
1055
1125
1056
1126
// return _hashValue(for: self)
1057
1127
auto *hashFunc = C.getHashValueForDecl ();
1058
- auto hashExpr = new (C) DeclRefExpr (hashFunc, DeclNameLoc (),
1128
+ if (!hashFunc->hasInterfaceType ())
1129
+ C.getLazyResolver ()->resolveDeclSignature (hashFunc);
1130
+
1131
+ auto selfType = hashValueDecl->mapTypeIntoContext (
1132
+ parentDC->getSelfInterfaceType ());
1133
+ auto hashableProto = C.getProtocol (KnownProtocolKind::Hashable);
1134
+ auto conformance = TypeChecker::conformsToProtocol (selfType, hashableProto,
1135
+ parentDC, None);
1136
+ auto subs = SubstitutionMap::get (hashFunc->getGenericSignature (),
1137
+ ArrayRef<Type>(selfType),
1138
+ ArrayRef<ProtocolConformanceRef>(*conformance));
1139
+ ConcreteDeclRef hashRef (hashFunc, subs);
1140
+
1141
+ auto hashExpr = new (C) DeclRefExpr (hashRef, DeclNameLoc (),
1059
1142
/* implicit*/ true );
1143
+
1144
+ Type intType = C.getIntDecl ()->getDeclaredType ();
1145
+ hashExpr->setType (FunctionType::get (AnyFunctionType::Param (selfType),
1146
+ intType));
1147
+
1060
1148
auto selfDecl = hashValueDecl->getImplicitSelfDecl ();
1061
1149
auto selfRef = new (C) DeclRefExpr (selfDecl, DeclNameLoc (),
1062
1150
/* implicit*/ true );
1063
- auto callExpr = CallExpr::createImplicit (C, hashExpr,
1064
- { selfRef }, { C.Id_for });
1151
+ selfRef->setType (selfType);
1152
+
1153
+ auto callExpr = CallExpr::createImplicit (C, hashExpr, { selfRef }, { });
1154
+ callExpr->setType (intType);
1155
+ callExpr->setThrows (false );
1156
+
1065
1157
auto returnStmt = new (C) ReturnStmt (SourceLoc (), callExpr);
1066
1158
1067
1159
auto body = BraceStmt::create (C, SourceLoc (), {returnStmt}, SourceLoc (),
1068
1160
/* implicit*/ true );
1069
1161
hashValueDecl->setBody (body);
1162
+ hashValueDecl->setBodyTypeCheckedIfPresent ();
1070
1163
}
1071
1164
1072
1165
// / Derive a 'hashValue' implementation.
@@ -1185,6 +1278,13 @@ bool DerivedConformance::canDeriveHashable(NominalTypeDecl *type) {
1185
1278
return true ;
1186
1279
}
1187
1280
1281
+ void DerivedConformance::tryDiagnoseFailedHashableDerivation (
1282
+ DeclContext *DC, NominalTypeDecl *nominal) {
1283
+ ASTContext &ctx = DC->getASTContext ();
1284
+ auto *hashableProto = ctx.getProtocol (KnownProtocolKind::Hashable);
1285
+ diagnoseFailedDerivation (DC, nominal, hashableProto);
1286
+ }
1287
+
1188
1288
ValueDecl *DerivedConformance::deriveHashable (ValueDecl *requirement) {
1189
1289
ASTContext &C = ConformanceDecl->getASTContext ();
1190
1290
@@ -1218,6 +1318,13 @@ ValueDecl *DerivedConformance::deriveHashable(ValueDecl *requirement) {
1218
1318
ConformanceDecl->diagnose (diag::type_does_not_conform,
1219
1319
Nominal->getDeclaredType (),
1220
1320
hashableProto->getDeclaredType ());
1321
+ // Ideally, this would be diagnosed in
1322
+ // ConformanceChecker::resolveWitnessViaLookup. That doesn't work for
1323
+ // Hashable because DerivedConformance::canDeriveHashable returns true
1324
+ // even if the conformance can't be derived. See the note there for
1325
+ // details.
1326
+ auto *dc = ConformanceDecl->getDeclContext ();
1327
+ tryDiagnoseFailedHashableDerivation (dc, Nominal);
1221
1328
return nullptr ;
1222
1329
}
1223
1330
0 commit comments