Merge pull request #13056 from xwu/much-of-a-muchness
[stdlib/Sema/AST] Adopt a commonly used implementation for combining hashes
diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h
index e611b58..57a60d6 100644
--- a/include/swift/AST/ASTContext.h
+++ b/include/swift/AST/ASTContext.h
@@ -482,8 +482,8 @@
FuncDecl *getEqualIntDecl() const;
/// Retrieve the declaration of
- /// Swift._mixForSynthesizedHashValue (Int, Int) -> Int.
- FuncDecl *getMixForSynthesizedHashValueDecl() const;
+ /// Swift._combineHashValues(Int, Int) -> Int.
+ FuncDecl *getCombineHashValuesDecl() const;
/// Retrieve the declaration of Swift._mixInt(Int) -> Int.
FuncDecl *getMixIntDecl() const;
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index c07980a..d53d188 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -194,8 +194,8 @@
/// func ==(Int, Int) -> Bool
FuncDecl *EqualIntDecl = nullptr;
- /// func _mixForSynthesizedHashValue(Int, Int) -> Int
- FuncDecl *MixForSynthesizedHashValueDecl = nullptr;
+ /// func _combineHashValues(Int, Int) -> Int
+ FuncDecl *CombineHashValuesDecl = nullptr;
/// func _mixInt(Int) -> Int
FuncDecl *MixIntDecl = nullptr;
@@ -1048,9 +1048,9 @@
return decl;
}
-FuncDecl *ASTContext::getMixForSynthesizedHashValueDecl() const {
- if (Impl.MixForSynthesizedHashValueDecl)
- return Impl.MixForSynthesizedHashValueDecl;
+FuncDecl *ASTContext::getCombineHashValuesDecl() const {
+ if (Impl.CombineHashValuesDecl)
+ return Impl.CombineHashValuesDecl;
auto resolver = getLazyResolver();
auto intType = getIntDecl()->getDeclaredType();
@@ -1066,8 +1066,8 @@
};
auto decl = lookupLibraryIntrinsicFunc(
- *this, "_mixForSynthesizedHashValue", resolver, callback);
- Impl.MixForSynthesizedHashValueDecl = decl;
+ *this, "_combineHashValues", resolver, callback);
+ Impl.CombineHashValuesDecl = decl;
return decl;
}
diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp
index 7a6bbea..d5b6ab6 100644
--- a/lib/Sema/DerivedConformanceEquatableHashable.cpp
+++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp
@@ -745,34 +745,34 @@
return integerExpr;
}
-/// Returns a new assignment expression that mixes the hash value of an
+/// Returns a new assignment expression that combines the hash value of an
/// expression into a variable.
/// \p C The AST context.
-/// \p resultVar The variable into which the hash value will be mixed.
-/// \p exprToHash The expression whose hash value should be mixed in.
-/// \return The expression that mixes the hash value into the result variable.
-static Expr* mixInHashExpr_hashValue(ASTContext &C,
- VarDecl* resultVar,
- Expr *exprToHash) {
+/// \p resultVar The variable into which the hash value will be combined.
+/// \p exprToHash The expression whose hash value should be combined.
+/// \return The expression that combines the hash value into the variable.
+static Expr* combineHashValuesAssignmentExpr(ASTContext &C,
+ VarDecl* resultVar,
+ Expr *exprToHash) {
// <exprToHash>.hashValue
auto hashValueExpr = new (C) UnresolvedDotExpr(exprToHash, SourceLoc(),
C.Id_hashValue, DeclNameLoc(),
/*implicit*/ true);
- // _mixForSynthesizedHashValue(result, <exprToHash>.hashValue)
- auto mixinFunc = C.getMixForSynthesizedHashValueDecl();
- auto mixinFuncExpr = new (C) DeclRefExpr(mixinFunc, DeclNameLoc(),
- /*implicit*/ true);
+ // _combineHashValues(result, <exprToHash>.hashValue)
+ auto combineFunc = C.getCombineHashValuesDecl();
+ auto combineFuncExpr = new (C) DeclRefExpr(combineFunc, DeclNameLoc(),
+ /*implicit*/ true);
auto rhsResultExpr = new (C) DeclRefExpr(resultVar, DeclNameLoc(),
/*implicit*/ true);
- auto mixinResultExpr = CallExpr::createImplicit(
- C, mixinFuncExpr, { rhsResultExpr, hashValueExpr }, {});
+ auto combineResultExpr = CallExpr::createImplicit(
+ C, combineFuncExpr, { rhsResultExpr, hashValueExpr }, {});
- // result = _mixForSynthesizedHashValue(result, <exprToHash>.hashValue)
+ // result = _combineHashValues(result, <exprToHash>.hashValue)
auto lhsResultExpr = new (C) DeclRefExpr(resultVar, DeclNameLoc(),
/*implicit*/ true);
auto assignExpr = new (C) AssignExpr(lhsResultExpr, SourceLoc(),
- mixinResultExpr, /*implicit*/ true);
+ combineResultExpr, /*implicit*/ true);
return assignExpr;
}
@@ -853,9 +853,9 @@
// If the enum has no associated values, we use the ordinal alone as the
// hash value, because that is sufficient for a good distribution. If any
- // case do have associated values, then the ordinal is used as the first
- // term mixed into _mixForSynthesizedHashValue, and the final result after
- // mixing in the payload is passed to _mixInt to improve the distribution.
+ // case does have associated values, then the ordinal is used as the first
+ // term combined into _combineHashValues, and the final result after
+ // combining the payload is passed to _mixInt to improve the distribution.
// result = <ordinal>
{
@@ -868,13 +868,14 @@
}
if (!hasNoAssociatedValues) {
- // Generate a sequence of expressions that mix the payload's hash values
- // into result.
+ // Generate a sequence of expressions that combine the payload's hash
+ // values into result.
for (auto payloadVar : payloadVars) {
auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(),
/*implicit*/ true);
- // result = _mixForSynthesizedHashValue(result, <payloadVar>.hashValue)
- auto mixExpr = mixInHashExpr_hashValue(C, resultVar, payloadVarRef);
+ // result = _combineHashValues(result, <payloadVar>.hashValue)
+ auto mixExpr = combineHashValuesAssignmentExpr(C, resultVar,
+ payloadVarRef);
mixExpressions.emplace_back(ASTNode(mixExpr));
}
@@ -953,7 +954,7 @@
auto storedProperties =
structDecl->getStoredProperties(/*skipInaccessible=*/true);
- // For each stored property, generate a statement that mixes its hash value
+ // For each stored property, generate a statement that combines its hash value
// into the result.
for (auto propertyDecl : storedProperties) {
auto propertyRef = new (C) DeclRefExpr(propertyDecl, DeclNameLoc(),
@@ -962,8 +963,9 @@
/*implicit*/ true);
auto selfPropertyExpr = new (C) DotSyntaxCallExpr(propertyRef, SourceLoc(),
selfRef);
- // result = _mixForSynthesizedHashValue(result, <property>.hashValue)
- auto mixExpr = mixInHashExpr_hashValue(C, resultVar, selfPropertyExpr);
+ // result = _combineHashValues(result, <property>.hashValue)
+ auto mixExpr = combineHashValuesAssignmentExpr(C, resultVar,
+ selfPropertyExpr);
statements.emplace_back(ASTNode(mixExpr));
}
@@ -1014,11 +1016,11 @@
// result = 0
// case B(let a0):
// result = 1
- // result = _mixForSynthesizedHashValue(result, a0.hashValue)
+ // result = _combineHashValues(result, a0.hashValue)
// case C(let a0, let a1):
// result = 2
- // result = _mixForSynthesizedHashValue(result, a0.hashValue)
- // result = _mixForSynthesizedHashValue(result, a1.hashValue)
+ // result = _combineHashValues(result, a0.hashValue)
+ // result = _combineHashValues(result, a1.hashValue)
// }
// result = _mixInt(result)
// return result
@@ -1030,8 +1032,8 @@
// var y: String
// @derived var hashValue: Int {
// var result: Int = 0
- // result = _mixForSynthesizedHashValue(result, x.hashValue)
- // result = _mixForSynthesizedHashValue(result, y.hashValue)
+ // result = _combineHashValues(result, x.hashValue)
+ // result = _combineHashValues(result, y.hashValue)
// result = _mixInt(result)
// return result
// }
diff --git a/stdlib/public/core/Hashing.swift b/stdlib/public/core/Hashing.swift
index 5a4ca72..116ba67 100644
--- a/stdlib/public/core/Hashing.swift
+++ b/stdlib/public/core/Hashing.swift
@@ -188,13 +188,21 @@
/// Returns a new value that combines the two given hash values.
///
+/// Combining is performed using [a hash function][ref] described by T.C. Hoad
+/// and J. Zobel, which is also adopted in the Boost C++ libraries.
+///
/// This function is used by synthesized implementations of `hashValue` to
/// combine the hash values of individual `struct` fields and associated values
/// of `enum`s. It is factored out into a standard library function so that the
/// specific hashing logic can be refined without requiring major changes to the
/// code that creates the synthesized AST nodes.
+///
+/// [ref]: http://goanna.cs.rmit.edu.au/~jz/fulltext/jasist-tch.pdf
@_transparent
public // @testable
-func _mixForSynthesizedHashValue(_ oldValue: Int, _ nextValue: Int) -> Int {
- return 31 &* oldValue &+ nextValue
+func _combineHashValues(_ firstValue: Int, _ secondValue: Int) -> Int {
+ let magic = 0x9e3779b9 as UInt // Based on the golden ratio.
+ var x = UInt(bitPattern: firstValue)
+ x ^= UInt(bitPattern: secondValue) &+ magic &+ (x &<< 6) &+ (x &>> 2)
+ return Int(bitPattern: x)
}