mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
201 lines
6.9 KiB
C++
201 lines
6.9 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Runtime/Config.h"
|
|
#include "swift/Basic/Lazy.h"
|
|
#include "swift/Runtime/Concurrent.h"
|
|
#include "swift/Runtime/Debug.h"
|
|
#include "swift/Runtime/Metadata.h"
|
|
#include "Private.h"
|
|
#include "SwiftValue.h"
|
|
#include "SwiftHashableSupport.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::hashable_support;
|
|
|
|
namespace {
|
|
struct HashableConformanceKey {
|
|
/// The lookup key, the metadata of a type that is possibly derived
|
|
/// from a type that conforms to `Hashable`.
|
|
const Metadata *derivedType;
|
|
};
|
|
|
|
struct HashableConformanceEntry {
|
|
/// The lookup key, the metadata of a type that is possibly derived
|
|
/// from a type that conforms to `Hashable`.
|
|
const Metadata *derivedType;
|
|
|
|
/// The highest (closest to the root) type in the superclass chain
|
|
/// that conforms to `Hashable`.
|
|
///
|
|
/// Always non-NULL. We don't cache negative responses so that we
|
|
/// don't have to deal with cache invalidation.
|
|
const Metadata *baseTypeThatConformsToHashable;
|
|
|
|
HashableConformanceEntry(HashableConformanceKey key,
|
|
const Metadata *baseTypeThatConformsToHashable)
|
|
: derivedType(key.derivedType),
|
|
baseTypeThatConformsToHashable(baseTypeThatConformsToHashable) {}
|
|
|
|
int compareWithKey(const HashableConformanceKey &key) const {
|
|
if (key.derivedType != derivedType) {
|
|
return (uintptr_t(key.derivedType) < uintptr_t(derivedType) ? -1 : 1);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
getExtraAllocationSize(HashableConformanceKey key,
|
|
const Metadata *baseTypeThatConformsToHashable) {
|
|
return 0;
|
|
}
|
|
};
|
|
} // end unnamed namesapce
|
|
|
|
// FIXME(performance): consider merging this cache into the regular
|
|
// protocol conformance cache.
|
|
static Lazy<ConcurrentMap<HashableConformanceEntry>> HashableConformances;
|
|
|
|
template<bool KnownToConformToHashable>
|
|
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
|
static const Metadata *findHashableBaseTypeImpl(const Metadata *type) {
|
|
// Check the cache first.
|
|
if (HashableConformanceEntry *entry =
|
|
HashableConformances->find(HashableConformanceKey{type})) {
|
|
return entry->baseTypeThatConformsToHashable;
|
|
}
|
|
if (!KnownToConformToHashable &&
|
|
!swift_conformsToProtocol(type, &_TMps8Hashable)) {
|
|
// Don't cache the negative response because we don't invalidate
|
|
// this cache when a new conformance is loaded dynamically.
|
|
return nullptr;
|
|
}
|
|
// By this point, `type` is known to conform to `Hashable`.
|
|
|
|
const Metadata *baseTypeThatConformsToHashable = type;
|
|
while (true) {
|
|
const Metadata *superclass =
|
|
_swift_class_getSuperclass(baseTypeThatConformsToHashable);
|
|
if (!superclass)
|
|
break;
|
|
if (!swift_conformsToProtocol(superclass, &_TMps8Hashable))
|
|
break;
|
|
baseTypeThatConformsToHashable = superclass;
|
|
}
|
|
HashableConformances->getOrInsert(HashableConformanceKey{type},
|
|
baseTypeThatConformsToHashable);
|
|
return baseTypeThatConformsToHashable;
|
|
}
|
|
|
|
/// Find the base type that introduces the `Hashable` conformance.
|
|
/// Because the provided type is known to conform to `Hashable`, this
|
|
/// function always returns non-null.
|
|
///
|
|
/// - Precondition: `type` conforms to `Hashable` (not checked).
|
|
const Metadata *swift::hashable_support::findHashableBaseTypeOfHashableType(
|
|
const Metadata *type) {
|
|
auto result =
|
|
findHashableBaseTypeImpl</*KnownToConformToHashable=*/ true>(type);
|
|
assert(result && "Known-hashable types should have a `Hashable` conformance.");
|
|
return result;
|
|
}
|
|
|
|
/// Find the base type that introduces the `Hashable` conformance.
|
|
/// If `type` does not conform to `Hashable`, `nullptr` is returned.
|
|
const Metadata *swift::hashable_support::findHashableBaseType(
|
|
const Metadata *type) {
|
|
return findHashableBaseTypeImpl</*KnownToConformToHashable=*/ false>(type);
|
|
}
|
|
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
|
|
extern "C" void _swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
|
|
const OpaqueValue *value,
|
|
const void *anyHashableResultPointer,
|
|
const Metadata *T,
|
|
const WitnessTable *hashableWT
|
|
);
|
|
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
|
|
extern "C" void _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
|
|
OpaqueValue *value,
|
|
const void *anyHashableResultPointer,
|
|
const Metadata *type,
|
|
const WitnessTable *hashableWT
|
|
) {
|
|
switch (type->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
id srcObject;
|
|
memcpy(&srcObject, value, sizeof(id));
|
|
// Do we have a _SwiftValue?
|
|
if (_SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject)) {
|
|
// If so, extract the boxed value and try to cast it.
|
|
const Metadata *unboxedType;
|
|
const OpaqueValue *unboxedValue;
|
|
std::tie(unboxedType, unboxedValue) =
|
|
getValueFromSwiftValue(srcSwiftValue);
|
|
|
|
if (auto unboxedHashableWT =
|
|
swift_conformsToProtocol(type, &_TMps8Hashable)) {
|
|
ValueBuffer unboxedCopyBuf;
|
|
auto unboxedValueCopy = unboxedType->vw_initializeBufferWithCopy(
|
|
&unboxedCopyBuf, const_cast<OpaqueValue *>(unboxedValue));
|
|
_swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
|
|
unboxedValueCopy, anyHashableResultPointer, unboxedType,
|
|
unboxedHashableWT);
|
|
unboxedType->vw_deallocateBuffer(&unboxedCopyBuf);
|
|
type->vw_destroy(value);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
_swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
|
|
value, anyHashableResultPointer,
|
|
findHashableBaseTypeOfHashableType(type),
|
|
hashableWT);
|
|
return;
|
|
}
|
|
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
_swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
|
|
value, anyHashableResultPointer, type, hashableWT);
|
|
return;
|
|
|
|
case MetadataKind::ErrorObject:
|
|
// ErrorObject metadata is not used for any Swift-level values, so
|
|
// this case is unreachable.
|
|
_failCorruptType(type);
|
|
|
|
case MetadataKind::Opaque:
|
|
case MetadataKind::Tuple:
|
|
case MetadataKind::Function:
|
|
case MetadataKind::Existential:
|
|
case MetadataKind::Metatype:
|
|
case MetadataKind::ExistentialMetatype:
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
// We assume that the value can not be an existential,
|
|
// because existentials can't conform to Hashable today.
|
|
//
|
|
// FIXME: handle generalized existentials when Swift has them.
|
|
_failCorruptType(type);
|
|
}
|
|
_failCorruptType(type);
|
|
}
|
|
|