Skip to content

Commit 37f54bc

Browse files
committed
stdlib: make AnyHashable(SwiftValue) unbox the value
Previously AnyHashable would consider SwiftValue to be a subclass of NSObject (which it is in practice) and return false when trying to compare an AnyHashable of a SwiftValue box to an AnyHashable of the unboxed value.
1 parent c928554 commit 37f54bc

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

stdlib/public/runtime/AnyHashableSupport.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/Runtime/Debug.h"
1717
#include "swift/Runtime/Metadata.h"
1818
#include "Private.h"
19+
#include "SwiftValue.h"
1920
#include "SwiftHashableSupport.h"
2021

2122
using namespace swift;
@@ -126,7 +127,7 @@ extern "C" void _swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
126127

127128
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
128129
extern "C" void _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
129-
const OpaqueValue *value,
130+
OpaqueValue *value,
130131
const void *anyHashableResultPointer,
131132
const Metadata *type,
132133
const WitnessTable *hashableWT
@@ -135,6 +136,32 @@ extern "C" void _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
135136
case MetadataKind::Class:
136137
case MetadataKind::ObjCClassWrapper:
137138
case MetadataKind::ForeignClass: {
139+
#if SWIFT_OBJC_INTEROP
140+
id srcObject;
141+
memcpy(&srcObject, value, sizeof(id));
142+
// Do we have a SwiftValue?
143+
if (SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject)) {
144+
// If so, extract the boxed value and try to cast it.
145+
const Metadata *unboxedType;
146+
const OpaqueValue *unboxedValue;
147+
std::tie(unboxedType, unboxedValue) =
148+
getValueFromSwiftValue(srcSwiftValue);
149+
150+
if (auto unboxedHashableWT =
151+
swift_conformsToProtocol(type, &_TMps8Hashable)) {
152+
ValueBuffer unboxedCopyBuf;
153+
auto unboxedValueCopy = unboxedType->vw_initializeBufferWithCopy(
154+
&unboxedCopyBuf, const_cast<OpaqueValue *>(unboxedValue));
155+
_swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
156+
unboxedValueCopy, anyHashableResultPointer, unboxedType,
157+
unboxedHashableWT);
158+
unboxedType->vw_deallocateBuffer(&unboxedCopyBuf);
159+
type->vw_destroy(value);
160+
return;
161+
}
162+
}
163+
#endif
164+
138165
_swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
139166
value, anyHashableResultPointer,
140167
findHashableBaseTypeOfHashableType(type),

validation-test/stdlib/AnyHashable.swift.gyb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,29 @@ AnyHashableTests.test("AnyHashable(${wrapped}).base") {
8888
}
8989
% end
9090

91+
#if _runtime(_ObjC)
92+
AnyHashableTests.test("AnyHashable(MinimalHashableValue, SwiftValue(MinimalHashableValue))/Hashable") {
93+
let xs = (0...5).flatMap {
94+
[ MinimalHashableValue($0, identity: 0),
95+
MinimalHashableValue($0, identity: 1) ]
96+
}
97+
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
98+
99+
let boxedXs = xs.flatMap {
100+
[ AnyHashable($0),
101+
AnyHashable(_bridgeAnythingToObjectiveC($0) as! NSObject) ]
102+
}
103+
for x in boxedXs {
104+
expectEqual(
105+
"MinimalHashableValue",
106+
String(describing: type(of: x.base)))
107+
}
108+
checkHashable(
109+
boxedXs,
110+
equalityOracle: { $0 / 4 == $1 / 4 })
111+
}
112+
#endif
113+
91114
% for wrapped in ['GenericMinimalHashableValue', 'GenericMinimalHashableClass']:
92115
% for payload in [ 'OpaqueValue<Int>', 'LifetimeTracked' ]:
93116
AnyHashableTests.test("AnyHashable(${wrapped}<OpaqueValue<Int>>)/Hashable") {

0 commit comments

Comments
 (0)