Files
swift-mirror/stdlib/public/runtime/AnyHashableSupport.cpp
Arnold Schwaighofer 1ba367ada8 Fix _swift_makeAnyHashableUpcastingToHashableBaseType to use the witness table of the just unboxed typed of a SwiftValue
This got exposed due to the change to use witness tables as part of the
metadata cache key.

The  Swift._ConcreteHashableBox<MinimalHashableValue> metadata created as
part of:

 AnyHashable(MinimalHashableValue(1))

and

 AnyHashable(_bridgeAnythingToObjectiveC(MinimalHashableValue(1)) as!  NSObject)

Would not be uniqued to the same metadata because the witness table of
NSObject that we queried before that change was not equal to the witness
table of MinimalHashableValue.

This is tested by the AnyHashable.swift.gyb test case.

rdar://24958043
2018-05-14 13:54:15 -07:00

226 lines
7.7 KiB
C++

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://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/HeapObject.h"
#include "swift/Runtime/Casting.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;
}
size_t getExtraAllocationSize() const {
return 0;
}
};
} // end unnamed namespace
// FIXME(performance): consider merging this cache into the regular
// protocol conformance cache.
static ConcurrentMap<HashableConformanceEntry, /*Destructor*/ false>
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, &HashableProtocolDescriptor)) {
// 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, &HashableProtocolDescriptor))
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);
}
// internal func _makeAnyHashableUsingDefaultRepresentation<H : Hashable>(
// of value: H,
// storingResultInto result: UnsafeMutablePointer<AnyHashable>)
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
void _swift_makeAnyHashableUsingDefaultRepresentation(
const OpaqueValue *value,
const void *anyHashableResultPointer,
const Metadata *T,
const WitnessTable *hashableWT
);
// public func _makeAnyHashableUpcastingToHashableBaseType<H : Hashable>(
// _ value: H,
// storingResultInto result: UnsafeMutablePointer<AnyHashable>)
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
void _swift_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(unboxedType, &HashableProtocolDescriptor)) {
#ifndef SWIFT_RUNTIME_ENABLE_GUARANTEED_NORMAL_ARGUMENTS
ValueBuffer unboxedCopyBuf;
// Allocate buffer.
OpaqueValue *unboxedValueCopy =
unboxedType->allocateBufferIn(&unboxedCopyBuf);
// initWithCopy.
unboxedType->vw_initializeWithCopy(
unboxedValueCopy, const_cast<OpaqueValue *>(unboxedValue));
_swift_makeAnyHashableUpcastingToHashableBaseType(
unboxedValueCopy, anyHashableResultPointer, unboxedType,
unboxedHashableWT);
// Deallocate buffer.
unboxedType->deallocateBufferIn(&unboxedCopyBuf);
type->vw_destroy(value);
#else
_swift_makeAnyHashableUpcastingToHashableBaseType(
const_cast<OpaqueValue *>(unboxedValue), anyHashableResultPointer,
unboxedType, unboxedHashableWT);
#endif
return;
}
}
#endif
_swift_makeAnyHashableUsingDefaultRepresentation(
value, anyHashableResultPointer,
findHashableBaseTypeOfHashableType(type),
hashableWT);
return;
}
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Optional:
_swift_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);
}