@@ -838,22 +838,7 @@ deriveHashable_hashInto(TypeChecker &tc, Decl *parentDecl,
838
838
FunctionType::ExtInfo ());
839
839
}
840
840
hashDecl->setInterfaceType (interfaceType);
841
-
842
- if (typeDecl->getFormalAccess () != AccessLevel::Private) {
843
- hashDecl->copyFormalAccessAndVersionedAttrFrom (typeDecl);
844
- } else {
845
- // FIXME: We want to call copyFormalAccessAndVersionedAttrFrom here, but we
846
- // can't copy the access level of a private type, because of the backhanded
847
- // way we synthesize _hash(into:) in deriveHashable -- we need to make sure
848
- // the resolver will find the new function, so it needs to be at least
849
- // fileprivate. (The access level of synthesized members doesn't normally
850
- // matter; they don't go through an access level check after being returned
851
- // from the synthesizer.)
852
- hashDecl->setAccess (AccessLevel::FilePrivate);
853
- if (typeDecl->getAttrs ().hasAttribute <VersionedAttr>()) {
854
- hashDecl->getAttrs ().add (new (C) VersionedAttr (/* implicit=*/ true ));
855
- }
856
- }
841
+ hashDecl->copyFormalAccessAndVersionedAttrFrom (typeDecl);
857
842
858
843
// If we aren't synthesizing into an imported/derived type, the derived conformance is
859
844
// either from the type itself or an extension, in which case we will emit the
@@ -873,6 +858,30 @@ deriveHashable_hashInto(TypeChecker &tc, Decl *parentDecl,
873
858
return hashDecl;
874
859
}
875
860
861
+ // / Derive the body for the _hash(into:) method when hashValue has a
862
+ // / user-supplied implementation.
863
+ static void
864
+ deriveBodyHashable_compat_hashInto (AbstractFunctionDecl *hashIntoDecl) {
865
+ // func _hash(into hasher: inout _Hasher) {
866
+ // hasher.append(self.hashValue)
867
+ // }
868
+ auto parentDC = hashIntoDecl->getDeclContext ();
869
+ ASTContext &C = parentDC->getASTContext ();
870
+
871
+ auto selfDecl = hashIntoDecl->getImplicitSelfDecl ();
872
+ auto selfRef = new (C) DeclRefExpr (selfDecl, DeclNameLoc (),
873
+ /* implicit*/ true );
874
+ auto hashValueExpr = new (C) UnresolvedDotExpr (selfRef, SourceLoc (),
875
+ C.Id_hashValue , DeclNameLoc (),
876
+ /* implicit*/ true );
877
+ auto hasherParam = hashIntoDecl->getParameterList (1 )->get (0 );
878
+ auto hasherExpr = createHasherAppendCall (C, hasherParam, hashValueExpr);
879
+
880
+ auto body = BraceStmt::create (C, SourceLoc (), {ASTNode (hasherExpr)},
881
+ SourceLoc (), /* implicit*/ true );
882
+ hashIntoDecl->setBody (body);
883
+ }
884
+
876
885
// / Derive the body for the '_hash(into:)' method for an enum.
877
886
static void
878
887
deriveBodyHashable_enum_hashInto (AbstractFunctionDecl *hashIntoDecl) {
@@ -1139,19 +1148,12 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl,
1139
1148
return hashValueDecl;
1140
1149
}
1141
1150
1142
- bool DerivedConformance::canDeriveHashable (TypeChecker &tc,
1143
- NominalTypeDecl *type,
1144
- ValueDecl *requirement) {
1145
- auto hashableProto = tc.Context .getProtocol (KnownProtocolKind::Hashable);
1146
- return canDeriveConformance (tc, type, hashableProto);
1147
- }
1148
-
1149
1151
static ValueDecl *
1150
- getHashIntoRequirement (ASTContext &C) {
1152
+ getHashValueRequirement (ASTContext &C) {
1151
1153
auto hashableProto = C.getProtocol (KnownProtocolKind::Hashable);
1152
1154
for (auto member: hashableProto->getMembers ()) {
1153
- if (auto fd = dyn_cast<FuncDecl >(member)) {
1154
- if (fd->getBaseName () == C.Id_hash )
1155
+ if (auto fd = dyn_cast<VarDecl >(member)) {
1156
+ if (fd->getBaseName () == C.Id_hashValue )
1155
1157
return fd;
1156
1158
}
1157
1159
}
@@ -1171,57 +1173,76 @@ getHashableConformance(Decl *parentDecl) {
1171
1173
return nullptr ;
1172
1174
}
1173
1175
1176
+ bool DerivedConformance::canDeriveHashable (TypeChecker &tc,
1177
+ NominalTypeDecl *type,
1178
+ ValueDecl *requirement) {
1179
+ if (!isa<EnumDecl>(type) && !isa<StructDecl>(type) && !isa<ClassDecl>(type))
1180
+ return false ;
1181
+ // FIXME: This is not actually correct. We cannot promise to always
1182
+ // provide a witness here in all cases. Unfortunately, figuring out
1183
+ // whether this is actually possible requires a parent decl context.
1184
+ // When the answer is no, DerivedConformance::deriveHashable will output
1185
+ // its own diagnostics.
1186
+ return true ;
1187
+ }
1188
+
1174
1189
ValueDecl *DerivedConformance::deriveHashable (TypeChecker &tc,
1175
1190
Decl *parentDecl,
1176
1191
NominalTypeDecl *type,
1177
1192
ValueDecl *requirement) {
1178
1193
ASTContext &C = parentDecl->getASTContext ();
1179
- auto hashableProto = C.getProtocol (KnownProtocolKind::Hashable);
1180
-
1181
- // Conformance can't be synthesized in an extension; we allow it as a special
1182
- // case for enums with no associated values to preserve source compatibility.
1183
- auto theEnum = dyn_cast<EnumDecl>(type);
1184
- if (!(theEnum && theEnum->hasOnlyCasesWithoutAssociatedValues ()) &&
1185
- type != parentDecl) {
1186
- auto hashableType = hashableProto->getDeclaredType ();
1187
- tc.diagnose (parentDecl->getLoc (), diag::cannot_synthesize_in_extension,
1188
- hashableType);
1189
- return nullptr ;
1190
- }
1191
1194
1192
1195
// var hashValue: Int
1193
1196
if (requirement->getBaseName () == C.Id_hashValue ) {
1194
- auto hashValueDecl = deriveHashable_hashValue (tc, parentDecl, type);
1195
-
1196
- // Also derive _hash(into:) -- it has a default implementation, so we
1197
- // wouldn't otherwise consider it as a candidate for synthesizing.
1198
- //
1199
- // FIXME: This assumes that _hash(into:) hasn't already been resolved. It
1200
- // would be nicer to remove the default implementation and independently
1201
- // synthesize either/both of the two Hashable requirements as needed;
1202
- // however, synthesizing methods (as opposed to properties) into imported
1203
- // types is not yet fully supported.
1204
- if (auto hashIntoReq = getHashIntoRequirement (C)) {
1205
- AbstractFunctionDecl::BodySynthesizer hashIntoSynthesizer;
1197
+ // We always allow hashValue to be synthesized; invalid cases are diagnosed
1198
+ // during _hash(into:) synthesis.
1199
+ return deriveHashable_hashValue (tc, parentDecl, type);
1200
+ }
1201
+
1202
+ // Hashable._hash(into:)
1203
+ if (requirement->getBaseName () == C.Id_hash ) {
1204
+ // Start by resolving hashValue conformance.
1205
+ auto hashValueReq = getHashValueRequirement (C);
1206
+ auto conformance = getHashableConformance (parentDecl);
1207
+ auto hashValueDecl = conformance->getWitnessDecl (hashValueReq, &tc);
1208
+
1209
+ if (hashValueDecl->isImplicit ()) {
1210
+ // Neither hashValue nor _hash(into:) is explicitly defined; we need to do
1211
+ // a full Hashable derivation.
1212
+
1213
+ // Refuse to synthesize Hashable if type isn't a struct or enum, or if it
1214
+ // has non-Hashable stored properties/associated values.
1215
+ auto hashableProto = C.getProtocol (KnownProtocolKind::Hashable);
1216
+ if (!canDeriveConformance (tc, type, hashableProto)) {
1217
+ tc.diagnose (parentDecl->getLoc (), diag::type_does_not_conform,
1218
+ type->getDeclaredType (), hashableProto->getDeclaredType ());
1219
+ return nullptr ;
1220
+ }
1221
+ // Hashable can't be fully synthesized in an extension; we allow it as a
1222
+ // special case for enums with no associated values to preserve source
1223
+ // compatibility.
1224
+ auto theEnum = dyn_cast<EnumDecl>(type);
1225
+ if (!(theEnum && theEnum->hasOnlyCasesWithoutAssociatedValues ()) &&
1226
+ type != parentDecl) {
1227
+ tc.diagnose (parentDecl->getLoc (), diag::cannot_synthesize_in_extension,
1228
+ hashableProto->getDeclaredType ());
1229
+ return nullptr ;
1230
+ }
1206
1231
if (theEnum)
1207
- hashIntoSynthesizer = &deriveBodyHashable_enum_hashInto;
1232
+ return deriveHashable_hashInto (tc, parentDecl, theEnum,
1233
+ &deriveBodyHashable_enum_hashInto);
1208
1234
else if (auto theStruct = dyn_cast<StructDecl>(type))
1209
- hashIntoSynthesizer = &deriveBodyHashable_struct_hashInto;
1235
+ return deriveHashable_hashInto (tc, parentDecl, theStruct,
1236
+ &deriveBodyHashable_struct_hashInto);
1210
1237
else
1211
1238
llvm_unreachable (" Attempt to derive Hashable for a type other "
1212
- " than a struct or enum" );
1213
- auto hashIntoDecl = deriveHashable_hashInto (tc, parentDecl, type,
1214
- hashIntoSynthesizer);
1215
- if (hashIntoDecl) {
1216
- auto conformance = getHashableConformance (parentDecl);
1217
- auto witnessDecl = conformance->getWitnessDecl (hashIntoReq, &tc);
1218
- if (witnessDecl != hashIntoDecl) {
1219
- tc.diagnose (requirement->getLoc (),
1220
- diag::broken_hashable_requirement);
1221
- }
1222
- }
1239
+ " than a struct or enum" );
1240
+ } else {
1241
+ // We can always derive _hash(into:) if hashValue has an explicit
1242
+ // implementation.
1243
+ return deriveHashable_hashInto (tc, parentDecl, type,
1244
+ &deriveBodyHashable_compat_hashInto);
1223
1245
}
1224
- return hashValueDecl;
1225
1246
}
1226
1247
1227
1248
tc.diagnose (requirement->getLoc (), diag::broken_hashable_requirement);
0 commit comments