Skip to content

Commit e3d0e48

Browse files
committed
[Sema] Switch synthesized hashing to use the new resilient hasher interface
Force-derive Hashable._hash(into:) whenever we need to derive hashValue. Do all the actual work there. _hash(into:) has a default implementation, so it wouldn’t normally be considered a requirement in need of synthesized conformance. However, for types that synthesize their hashability, we want to replace the default with a custom implementation. A better option would be to remove the default implementation and synthesize it (and/or hashValue) as needed: 1. If a witness hashValue cannot be resolved, synthesize one that simply returns _hashValue(for: self). (I.e., express hashValue in terms of _hash(into:).) 2. If a witness for _hash(into:) cannot be resolved: a) if there is an explicit witness for hashValue, synthesize _hash(into:) such that it simply feeds the hashValue to the hasher. b) otherwise if the parent context is not a struct or enum decl, then fail and produce a diagnostic. We can’t fully synthesize Hashable for classes or in extensions. c) if the components of the type aren’t all Hashable, then fail and produce a diagnostic about a missing hashValue implementation. c) otherwise synthesize _hash(into:) by feeding the type’s components into the hasher. However, the DerivedConformance is not yet a great fit for such complicated derivation rules, so we’d need to pretend Hashable is always derivable, and manually provide diagnostics in cases 2/b and 2/c. Providing high-quality, non-duplicated diagnostics is a challenge. Even worse, case 2/a would require us to support synthesizing _hash(into:) into extensions, classes, synthesized structs and imported types — and unfortunately, compiler support for doing this is currently limited.
1 parent 6ae2fc9 commit e3d0e48

File tree

5 files changed

+313
-238
lines changed

5 files changed

+313
-238
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ class ASTContext {
400400
ProtocolDecl *getErrorDecl() const;
401401
CanType getExceptionType() const;
402402

403-
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
404-
/** Retrieve the declaration of Swift.NAME. */ \
403+
#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \
404+
/** Retrieve the declaration of Swift.IDSTR. */ \
405405
DECL_CLASS *get##NAME##Decl() const;
406406
#include "swift/AST/KnownStdlibTypes.def"
407407

@@ -471,12 +471,8 @@ class ASTContext {
471471
/// Retrieve the declaration of Swift.==(Int, Int) -> Bool.
472472
FuncDecl *getEqualIntDecl() const;
473473

474-
/// Retrieve the declaration of
475-
/// Swift._combineHashValues(Int, Int) -> Int.
476-
FuncDecl *getCombineHashValuesDecl() const;
477-
478-
/// Retrieve the declaration of Swift._mixInt(Int) -> Int.
479-
FuncDecl *getMixIntDecl() const;
474+
/// Retrieve the declaration of Swift._hashValue<H>(for: H) -> Int.
475+
FuncDecl *getHashValueForDecl() const;
480476

481477
/// Retrieve the declaration of Array.append(element:)
482478
FuncDecl *getArrayAppendElementDecl() const;

include/swift/AST/KnownIdentifiers.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ IDENTIFIER(alloc)
2626
IDENTIFIER(allocWithZone)
2727
IDENTIFIER(allZeros)
2828
IDENTIFIER(Any)
29+
IDENTIFIER(appending)
2930
IDENTIFIER(ArrayLiteralElement)
3031
IDENTIFIER(atIndexedSubscript)
3132
IDENTIFIER_(bridgeToObjectiveC)
@@ -54,14 +55,18 @@ IDENTIFIER(encoder)
5455
IDENTIFIER(error)
5556
IDENTIFIER(forKeyedSubscript)
5657
IDENTIFIER(Foundation)
58+
IDENTIFIER(for)
5759
IDENTIFIER(forKey)
5860
IDENTIFIER(from)
5961
IDENTIFIER(fromRaw)
62+
IDENTIFIER_(hash)
63+
IDENTIFIER(hasher)
6064
IDENTIFIER(hashValue)
6165
IDENTIFIER(init)
6266
IDENTIFIER(initialize)
6367
IDENTIFIER(initStorage)
6468
IDENTIFIER(initialValue)
69+
IDENTIFIER(into)
6570
IDENTIFIER(intValue)
6671
IDENTIFIER(Key)
6772
IDENTIFIER(KeyedDecodingContainer)

include/swift/AST/KnownStdlibTypes.def

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18-
#ifndef KNOWN_STDLIB_TYPE_DECL
19-
/// KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS)
18+
#ifndef KNOWN_STDLIB_TYPE_DECL_WITH_NAME
19+
/// KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS)
2020
///
2121
/// The macro is expanded for each known standard library type. NAME is
2222
/// bound to the unqualified name of the type. DECL_CLASS is bound to the
2323
/// Decl subclass it is expected to be an instance of. NUM_GENERIC_PARAMS is
2424
/// bound to the number of generic parameters the type is expected to have.
25-
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS)
25+
#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS)
2626
#endif
2727

28+
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
29+
KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, #NAME, DECL_CLASS, NUM_GENERIC_PARAMS)
30+
2831
KNOWN_STDLIB_TYPE_DECL(Bool, NominalTypeDecl, 0)
2932

3033
KNOWN_STDLIB_TYPE_DECL(Int, NominalTypeDecl, 0)
@@ -50,6 +53,7 @@ KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1)
5053
KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2)
5154
KNOWN_STDLIB_TYPE_DECL(AnyHashable, NominalTypeDecl, 0)
5255
KNOWN_STDLIB_TYPE_DECL(MutableCollection, ProtocolDecl, 1)
56+
KNOWN_STDLIB_TYPE_DECL_WITH_NAME(UnsafeHasher, "_UnsafeHasher", NominalTypeDecl, 0)
5357

5458
KNOWN_STDLIB_TYPE_DECL(AnyKeyPath, NominalTypeDecl, 0)
5559
KNOWN_STDLIB_TYPE_DECL(PartialKeyPath, NominalTypeDecl, 1)
@@ -79,3 +83,4 @@ KNOWN_STDLIB_TYPE_DECL(KeyedDecodingContainer, NominalTypeDecl, 1)
7983
KNOWN_STDLIB_TYPE_DECL(RangeReplaceableCollection, ProtocolDecl, 1)
8084

8185
#undef KNOWN_STDLIB_TYPE_DECL
86+
#undef KNOWN_STDLIB_TYPE_DECL_WITH_NAME

lib/AST/ASTContext.cpp

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ struct ASTContext::Implementation {
135135
/// The AnyObject type.
136136
CanType AnyObjectType;
137137

138-
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
138+
#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \
139139
/** The declaration of Swift.NAME. */ \
140140
DECL_CLASS *NAME##Decl = nullptr;
141141
#include "swift/AST/KnownStdlibTypes.def"
@@ -189,12 +189,9 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL)
189189
/// func ==(Int, Int) -> Bool
190190
FuncDecl *EqualIntDecl = nullptr;
191191

192-
/// func _combineHashValues(Int, Int) -> Int
193-
FuncDecl *CombineHashValuesDecl = nullptr;
192+
/// func _hashValue<H: Hashable>(for: H) -> Int
193+
FuncDecl *HashValueForDecl = nullptr;
194194

195-
/// func _mixInt(Int) -> Int
196-
FuncDecl *MixIntDecl = nullptr;
197-
198195
/// func append(Element) -> void
199196
FuncDecl *ArrayAppendElementDecl = nullptr;
200197

@@ -616,11 +613,11 @@ FuncDecl *ASTContext::getPlusFunctionOnString() const {
616613
return Impl.PlusFunctionOnString;
617614
}
618615

619-
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
616+
#define KNOWN_STDLIB_TYPE_DECL_WITH_NAME(NAME, IDSTR, DECL_CLASS, NUM_GENERIC_PARAMS) \
620617
DECL_CLASS *ASTContext::get##NAME##Decl() const { \
621618
if (!Impl.NAME##Decl) \
622619
Impl.NAME##Decl = dyn_cast_or_null<DECL_CLASS>( \
623-
findStdlibType(*this, #NAME, NUM_GENERIC_PARAMS)); \
620+
findStdlibType(*this, IDSTR, NUM_GENERIC_PARAMS)); \
624621
return Impl.NAME##Decl; \
625622
}
626623
#include "swift/AST/KnownStdlibTypes.def"
@@ -989,44 +986,29 @@ FuncDecl *ASTContext::getGetBoolDecl(LazyResolver *resolver) const {
989986
return decl;
990987
}
991988

992-
FuncDecl *ASTContext::getCombineHashValuesDecl() const {
993-
if (Impl.CombineHashValuesDecl)
994-
return Impl.CombineHashValuesDecl;
995-
996-
auto resolver = getLazyResolver();
997-
auto intType = getIntDecl()->getDeclaredType();
998-
999-
auto callback = [&](Type inputType, Type resultType) {
1000-
// Look for the signature (Int, Int) -> Int
1001-
auto tupleType = dyn_cast<TupleType>(inputType.getPointer());
1002-
assert(tupleType);
1003-
return tupleType->getNumElements() == 2 &&
1004-
tupleType->getElementType(0)->isEqual(intType) &&
1005-
tupleType->getElementType(1)->isEqual(intType) &&
1006-
resultType->isEqual(intType);
1007-
};
1008-
1009-
auto decl = lookupLibraryIntrinsicFunc(
1010-
*this, "_combineHashValues", resolver, callback);
1011-
Impl.CombineHashValuesDecl = decl;
1012-
return decl;
1013-
}
1014-
1015-
FuncDecl *ASTContext::getMixIntDecl() const {
1016-
if (Impl.MixIntDecl)
1017-
return Impl.MixIntDecl;
989+
FuncDecl *ASTContext::getHashValueForDecl() const {
990+
if (Impl.HashValueForDecl)
991+
return Impl.HashValueForDecl;
1018992

1019-
auto resolver = getLazyResolver();
1020-
auto intType = getIntDecl()->getDeclaredType();
1021-
1022-
auto callback = [&](Type inputType, Type resultType) {
1023-
// Look for the signature (Int) -> Int
1024-
return inputType->isEqual(intType) && resultType->isEqual(intType);
1025-
};
1026-
1027-
auto decl = lookupLibraryIntrinsicFunc(*this, "_mixInt", resolver, callback);
1028-
Impl.MixIntDecl = decl;
1029-
return decl;
993+
SmallVector<ValueDecl *, 1> results;
994+
lookupInSwiftModule("_hashValue", results);
995+
for (auto result : results) {
996+
auto *fd = dyn_cast<FuncDecl>(result);
997+
if (!fd)
998+
continue;
999+
auto paramLists = fd->getParameterLists();
1000+
if (paramLists.size() != 1 || paramLists[0]->size() != 1)
1001+
continue;
1002+
auto paramDecl = paramLists[0]->get(0);
1003+
if (paramDecl->getArgumentName() != Id_for)
1004+
continue;
1005+
auto genericParams = fd->getGenericParams();
1006+
if (!genericParams || genericParams->size() != 1)
1007+
continue;
1008+
Impl.HashValueForDecl = fd;
1009+
return fd;
1010+
}
1011+
return nullptr;
10301012
}
10311013

10321014
FuncDecl *ASTContext::getArrayAppendElementDecl() const {

0 commit comments

Comments
 (0)