Skip to content

Commit 5e03e33

Browse files
committed
[Compatibility53] Backport Tuple Hashable Conformance
1 parent 4ff28f2 commit 5e03e33

File tree

2 files changed

+149
-3
lines changed

2 files changed

+149
-3
lines changed

stdlib/toolchain/Compatibility53/BuiltinProtocolConformances.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ static const ProtocolDescriptor *getComparableDescriptor() {
3737
return descriptor;
3838
}
3939

40+
static const ProtocolDescriptor *getHashableDescriptor() {
41+
auto descriptor = SWIFT_LAZY_CONSTANT(
42+
reinterpret_cast<const ProtocolDescriptor *>(
43+
dlsym(RTLD_DEFAULT, "$sSHMp")));
44+
return descriptor;
45+
}
46+
4047
static const WitnessTable *conformsToProtocol(const Metadata *type,
4148
const ProtocolDescriptor *protocol) {
4249
using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *);
@@ -449,3 +456,130 @@ bool swift::_swift_tupleComparable_greaterThan(OpaqueValue *tuple1,
449456
// each other.
450457
return false;
451458
}
459+
460+
//===----------------------------------------------------------------------===//
461+
// Tuple Hashable Conformance
462+
//===----------------------------------------------------------------------===//
463+
464+
#define TUPLE_HASHABLE_WT SYMBOL("_swift_tupleHashable_wt")
465+
466+
// Define the conformance descriptor for tuple Hashable. We do this in
467+
// assembly to work around relative reference issues.
468+
__asm(
469+
" .section __DATA,__data\n"
470+
" .globl " TUPLE_HASHABLE_CONF "\n"
471+
" .p2align 2\n"
472+
TUPLE_HASHABLE_CONF ":\n"
473+
// This is an indirectable relative reference to the Hashable protocol
474+
// descriptor. However, this is 0 here because the compatibility libraries
475+
// can't have a dependency on libswiftCore (which is where Hashable lives).
476+
" .long 0\n"
477+
// 769 is the MetadataKind::Tuple
478+
" .long 769\n"
479+
// This is a direct relative reference to the witness table defined below.
480+
" .long ((" TUPLE_HASHABLE_WT ") - (" TUPLE_HASHABLE_CONF ")) - 8\n"
481+
// 32 are the ConformanceFlags with the type reference bit set to MetadataKind.
482+
" .long 32\n"
483+
);
484+
485+
extern const ProtocolConformanceDescriptor _swift_tupleHashable_conf;
486+
487+
// Due to the fact that the compatibility libraries can't have a hard
488+
// dependency to libswiftCore (which is where the Hashable protocol desciptor
489+
// lives), we have to manually implant this before calling any user code.
490+
__attribute__((constructor))
491+
void _emplaceTupleHashableDescriptor() {
492+
auto tupleHashableConf = const_cast<int32_t *>(
493+
reinterpret_cast<const int32_t *>(&_swift_tupleHashable_conf));
494+
auto hashable = getHashableDescriptor();
495+
496+
// This is an indirectable pointer.
497+
*tupleHashableConf = intptr_t(hashable) - intptr_t(tupleHashableConf);
498+
}
499+
500+
// The base Equatable protocol is itself a requirement, thus the requirement
501+
// count is 4 (Equatable + hashValue + hash(into:) + _rawHashValue) and the
502+
// witness is the tuple Equatable table.
503+
SWIFT_RUNTIME_EXPORT
504+
_WitnessTable<4> _swift_tupleHashable_wt = {
505+
&_swift_tupleHashable_conf,
506+
{
507+
reinterpret_cast<const void *>(&_swift_tupleEquatable_wt),
508+
reinterpret_cast<void *>(_swift_tupleHashable_hashValue),
509+
reinterpret_cast<void *>(_swift_tupleHashable_hash),
510+
nullptr
511+
}
512+
};
513+
514+
static void *get_rawHashValueDefaultImplFunc() {
515+
auto impl = SWIFT_LAZY_CONSTANT(
516+
dlsym(RTLD_DEFAULT, "$sSHsE13_rawHashValue4seedS2i_tF"));
517+
return impl;
518+
}
519+
520+
// Due to the fact that the compatibility libraries can't have a hard
521+
// dependency to libswiftCore (which is where the _rawHashValue default impl
522+
// lives), we have to manually implant this before calling any user code.
523+
__attribute__((constructor))
524+
void _emplaceTupleHashable_rawHashValueDefaultImpl() {
525+
_swift_tupleHashable_wt.Witnesses[3] = get_rawHashValueDefaultImplFunc();
526+
}
527+
528+
using HashValueFn = SWIFT_CC(swift) intptr_t(OpaqueValue *value, Metadata *Self,
529+
void *witnessTable);
530+
using HasherCombineFn = SWIFT_CC(swift) void(OpaqueValue *value,
531+
const Metadata *Self,
532+
const WitnessTable *witnessTable,
533+
SWIFT_CONTEXT OpaqueValue *hasher);
534+
535+
static HashValueFn *get_hashValueFunc() {
536+
auto descriptor = SWIFT_LAZY_CONSTANT(
537+
reinterpret_cast<HashValueFn *>(
538+
dlsym(RTLD_DEFAULT, STR(SWIFT_HASHVALUE_FUNC))));
539+
return descriptor;
540+
}
541+
542+
static HasherCombineFn *getHashCombineFunc() {
543+
auto descriptor = SWIFT_LAZY_CONSTANT(
544+
reinterpret_cast<HasherCombineFn *>(
545+
dlsym(RTLD_DEFAULT, STR(SWIFT_HASHER_COMBINE_FUNC))));
546+
return descriptor;
547+
}
548+
549+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
550+
intptr_t swift::_swift_tupleHashable_hashValue(SWIFT_CONTEXT OpaqueValue *tuple,
551+
Metadata *Self, void *witnessTable) {
552+
auto _hashValue = get_hashValueFunc();
553+
return _hashValue(tuple, Self, witnessTable);
554+
}
555+
556+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
557+
void swift::_swift_tupleHashable_hash(OpaqueValue *hasher,
558+
SWIFT_CONTEXT OpaqueValue *tuple,
559+
Metadata *Self, void *witnessTable) {
560+
auto tupleTy = cast<TupleTypeMetadata>(Self);
561+
562+
// Loop through all elements and hash them into the Hasher.
563+
for (size_t i = 0; i != tupleTy->NumElements; i += 1) {
564+
auto elt = tupleTy->getElement(i);
565+
566+
// Ensure we actually have a conformance to Hashable for this element type.
567+
auto hashable = getHashableDescriptor();
568+
auto conformance = conformsToProtocol(elt.Type, hashable);
569+
570+
// If we don't have a conformance then something somewhere messed up in
571+
// deciding that this tuple type was Hashable...??
572+
if (!conformance)
573+
swift_unreachable("Tuple hasing requires that all elements be Hashable.");
574+
575+
// Get the element value from the tuple.
576+
auto value = reinterpret_cast<OpaqueValue *>(
577+
reinterpret_cast<char *>(tuple) + elt.Offset);
578+
579+
auto hasherCombine = getHashCombineFunc();
580+
581+
// Call the combine function on the hasher for this element value and we're
582+
// done!
583+
hasherCombine(value, elt.Type, conformance, hasher);
584+
}
585+
}

stdlib/toolchain/Compatibility53/ProtocolConformance.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ static const ProtocolDescriptor *getComparableDescriptor() {
3939
return descriptor;
4040
}
4141

42+
static const ProtocolDescriptor *getHashableDescriptor() {
43+
auto descriptor = SWIFT_LAZY_CONSTANT(
44+
reinterpret_cast<const ProtocolDescriptor *>(
45+
dlsym(RTLD_DEFAULT, "$sSHMp")));
46+
return descriptor;
47+
}
48+
4249
static const WitnessTable *conformsToProtocol(const Metadata *type,
4350
const ProtocolDescriptor *protocol) {
4451
using Fn = const WitnessTable *(const Metadata *, const ProtocolDescriptor *);
@@ -52,10 +59,11 @@ static bool tupleConformsToProtocol(const Metadata *type,
5259
const ProtocolDescriptor *protocol) {
5360
auto tuple = cast<TupleTypeMetadata>(type);
5461

55-
// At the moment, tuples can only conform to Equatable and Comparable, so
56-
// reject all other protocols.
62+
// At the moment, tuples can only conform to Equatable, Comparable, and
63+
// Hashable, so reject all other protocols.
5764
if (protocol != getEquatableDescriptor() &&
58-
protocol != getComparableDescriptor())
65+
protocol != getComparableDescriptor() &&
66+
protocol != getHashableDescriptor())
5967
return false;
6068

6169
for (size_t i = 0; i != tuple->NumElements; i += 1) {
@@ -69,6 +77,7 @@ static bool tupleConformsToProtocol(const Metadata *type,
6977

7078
extern const WitnessTable _swift_tupleEquatable_wt;
7179
extern const WitnessTable _swift_tupleComparable_wt;
80+
extern const WitnessTable _swift_tupleHashable_wt;
7281

7382
static const WitnessTable *getTupleConformanceWitnessTable(
7483
const ProtocolDescriptor *protocol) {
@@ -78,6 +87,9 @@ static const WitnessTable *getTupleConformanceWitnessTable(
7887
if (protocol == getComparableDescriptor())
7988
return &_swift_tupleComparable_wt;
8089

90+
if (protocol == getHashableDescriptor())
91+
return &_swift_tupleHashable_wt;
92+
8193
return nullptr;
8294
}
8395

0 commit comments

Comments
 (0)