mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Fixes rdar://154123172 (Consider memoizing the lookup for NSString's conformance to Hashable)
2727 lines
104 KiB
C++
2727 lines
104 KiB
C++
//===--- DynamicCast.cpp - Swift Language Dynamic Casting Support ---------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Implementations of the dynamic cast runtime functions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "../CompatibilityOverride/CompatibilityOverride.h"
|
|
#include "ErrorObject.h"
|
|
#include "Private.h"
|
|
#include "SwiftHashableSupport.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
#include "swift/Basic/Lazy.h"
|
|
#include "swift/Runtime/Bincompat.h"
|
|
#include "swift/Runtime/Casting.h"
|
|
#include "swift/Runtime/Config.h"
|
|
#include "swift/Runtime/ExistentialContainer.h"
|
|
#include "swift/Runtime/HeapObject.h"
|
|
#if SWIFT_OBJC_INTEROP
|
|
#include "swift/Runtime/ObjCBridge.h"
|
|
#include "SwiftObject.h"
|
|
#include "SwiftValue.h"
|
|
#endif
|
|
|
|
using namespace swift;
|
|
using namespace hashable_support;
|
|
|
|
//
|
|
// The top-level driver code directly handles the most general cases
|
|
// (identity casts, _ObjectiveCBridgeable, _SwiftValue boxing) and
|
|
// recursively unwraps source and/or destination as appropriate.
|
|
// It calls "tryCastToXyz" functions to perform tailored operations
|
|
// for a particular destination type.
|
|
//
|
|
// For each kind of destination, there is a "tryCastToXyz" that
|
|
// accepts a source value and attempts to fit it into a destination
|
|
// storage location. This function should assume that:
|
|
// * The source and destination types are _not_ identical.
|
|
// * The destination is of the expected type.
|
|
// * The source is already fully unwrapped. If the source is an
|
|
// Existential or Optional that you cannot handle directly, do _not_
|
|
// try to unwrap it. Just return failure and you will get called
|
|
// again with the unwrapped source.
|
|
//
|
|
// Each such function accepts the following arguments:
|
|
// * Destination location and type
|
|
// * Source value address and type
|
|
// * References to the types that will be used to report failure.
|
|
// The function can update these with specific failing types
|
|
// to improve the reported failure.
|
|
// * Bool indicating whether the compiler has asked us to "take" the
|
|
// value instead of copying.
|
|
// * Bool indicating whether it's okay to do type checks lazily on later
|
|
// access (this is permitted only for unconditional casts that will
|
|
// abort the program on failure anyway).
|
|
//
|
|
// The return value is one of the following:
|
|
// * Failure. In this case, the tryCastFunction should do nothing; your
|
|
// caller will either try another strategy or report the failure and
|
|
// do any necessary cleanup.
|
|
// * Success via "copy". You successfully copied the source value.
|
|
// * Success via "take". If "take" was requested and you can do so cheaply,
|
|
// perform the take and return SuccessViaTake. If "take" is not cheap, you
|
|
// should copy and return SuccessViaCopy. Top-level code will detect this
|
|
// and take care of destroying the source for you.
|
|
//
|
|
enum class DynamicCastResult {
|
|
Failure, /// The cast attempt "failed" (did nothing).
|
|
SuccessViaCopy, /// Cast succeeded, source is still valid.
|
|
SuccessViaTake, /// Cast succeeded, source is invalid
|
|
};
|
|
static bool isSuccess(DynamicCastResult result) {
|
|
return result != DynamicCastResult::Failure;
|
|
}
|
|
|
|
// All of our `tryCastXyz` functions have the following signature.
|
|
typedef DynamicCastResult (tryCastFunctionType)(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances
|
|
);
|
|
|
|
// Forward-declare the main top-level `tryCast()` function
|
|
static tryCastFunctionType tryCast;
|
|
|
|
/// Nominal type descriptor for Swift.AnyHashable
|
|
extern "C" const StructDescriptor STRUCT_TYPE_DESCR_SYM(s11AnyHashable);
|
|
|
|
/// Nominal type descriptor for Swift.__SwiftValue
|
|
//extern "C" const StructDescriptor STRUCT_TYPE_DESCR_SYM(s12__SwiftValue);
|
|
|
|
/// Nominal type descriptor for Swift.Array.
|
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sa);
|
|
|
|
/// Nominal type descriptor for Swift.Dictionary.
|
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SD);
|
|
|
|
/// Nominal type descriptor for Swift.Set.
|
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sh);
|
|
|
|
/// Nominal type descriptor for Swift.String.
|
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
|
|
|
|
/// This issues a fatal error or warning if the srcValue contains a null object
|
|
/// reference. It is used when the srcType is a non-nullable reference type, in
|
|
/// which case it is dangerous to continue with a null reference. The null
|
|
/// reference is returned if we're operating in backwards-compatibility mode, so
|
|
/// callers still have to check for null.
|
|
static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
|
|
const Metadata *srcType,
|
|
const Metadata *destType) {
|
|
auto object = *reinterpret_cast<HeapObject **>(srcValue);
|
|
if (LLVM_LIKELY(object != nullptr)) {
|
|
return object;
|
|
}
|
|
|
|
std::string srcTypeName = nameForMetadata(srcType);
|
|
std::string destTypeName = nameForMetadata(destType);
|
|
const char * const msg = "Found a null pointer in a value of type '%s' (%p)."
|
|
" Non-Optional values are not allowed to hold null pointers."
|
|
" (Detected while casting to '%s' (%p))%s\n";
|
|
if (runtime::bincompat::useLegacyPermissiveObjCNullSemanticsInCasting()) {
|
|
// In backwards compatibility mode, this code will warn and return the null
|
|
// reference anyway: If you examine the calls to the function, you'll see
|
|
// that most callers fail the cast in that case, but a few casts (e.g., with
|
|
// Obj-C or CF destination type) sill succeed in that case. This is
|
|
// dangerous, but necessary for compatibility.
|
|
swift::warning(/* flags = */ 0, msg,
|
|
srcTypeName.c_str(), srcType,
|
|
destTypeName.c_str(), destType,
|
|
": Continuing with null object, but expect problems later.");
|
|
} else {
|
|
// By default, Swift 5.4 and later issue a fatal error.
|
|
swift::fatalError(/* flags = */ 0, msg,
|
|
srcTypeName.c_str(), srcType,
|
|
destTypeName.c_str(), destType,
|
|
"");
|
|
}
|
|
return object;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/******************************* Bridge Helpers *******************************/
|
|
/******************************************************************************/
|
|
|
|
#define _bridgeAnythingToObjectiveC \
|
|
MANGLE_SYM(s27_bridgeAnythingToObjectiveCyyXlxlF)
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
|
|
HeapObject *_bridgeAnythingToObjectiveC(
|
|
OpaqueValue *src, const Metadata *srcType);
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
SWIFT_RUNTIME_EXPORT
|
|
id swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype);
|
|
#endif
|
|
|
|
// protocol _ObjectiveCBridgeable {
|
|
struct _ObjectiveCBridgeableWitnessTable : WitnessTable {
|
|
#define _protocolWitnessSignedPointer(n) \
|
|
__ptrauth_swift_protocol_witness_function_pointer(SpecialPointerAuthDiscriminators::n##Discriminator) n
|
|
|
|
static_assert(WitnessTableFirstRequirementOffset == 1,
|
|
"Witness table layout changed");
|
|
|
|
// associatedtype _ObjectiveCType : class
|
|
void *_ObjectiveCType;
|
|
|
|
// func _bridgeToObjectiveC() -> _ObjectiveCType
|
|
SWIFT_CC(swift)
|
|
HeapObject *(*_protocolWitnessSignedPointer(bridgeToObjectiveC))(
|
|
SWIFT_CONTEXT OpaqueValue *self, const Metadata *Self,
|
|
const _ObjectiveCBridgeableWitnessTable *witnessTable);
|
|
|
|
// class func _forceBridgeFromObjectiveC(x: _ObjectiveCType,
|
|
// inout result: Self?)
|
|
SWIFT_CC(swift)
|
|
void (*_protocolWitnessSignedPointer(forceBridgeFromObjectiveC))(
|
|
HeapObject *sourceValue,
|
|
OpaqueValue *result,
|
|
SWIFT_CONTEXT const Metadata *self,
|
|
const Metadata *selfType,
|
|
const _ObjectiveCBridgeableWitnessTable *witnessTable);
|
|
|
|
// class func _conditionallyBridgeFromObjectiveC(x: _ObjectiveCType,
|
|
// inout result: Self?) -> Bool
|
|
SWIFT_CC(swift)
|
|
bool (*_protocolWitnessSignedPointer(conditionallyBridgeFromObjectiveC))(
|
|
HeapObject *sourceValue,
|
|
OpaqueValue *result,
|
|
SWIFT_CONTEXT const Metadata *self,
|
|
const Metadata *selfType,
|
|
const _ObjectiveCBridgeableWitnessTable *witnessTable);
|
|
};
|
|
// }
|
|
|
|
extern "C" const ProtocolDescriptor
|
|
PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable);
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
#define BRIDGING_CONFORMANCE_SYM \
|
|
MANGLE_SYM(s19_BridgeableMetatypeVs21_ObjectiveCBridgeablesWP)
|
|
|
|
extern "C" const _ObjectiveCBridgeableWitnessTable BRIDGING_CONFORMANCE_SYM;
|
|
#endif
|
|
|
|
/// Nominal type descriptor for Swift.String.
|
|
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
|
|
|
|
struct ObjCBridgeWitnessCacheEntry {
|
|
const Metadata *metadata;
|
|
const _ObjectiveCBridgeableWitnessTable *witness;
|
|
};
|
|
|
|
static const _ObjectiveCBridgeableWitnessTable *
|
|
swift_conformsToObjectiveCBridgeableNoCache(const Metadata *T) {
|
|
auto w = swift_conformsToProtocolCommon(
|
|
T, &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable));
|
|
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
|
}
|
|
|
|
static const _ObjectiveCBridgeableWitnessTable *
|
|
swift_conformsToObjectiveCBridgeable(const Metadata *T) {
|
|
static std::atomic<ObjCBridgeWitnessCacheEntry> _objcBridgeWitnessCache = {};
|
|
auto cached = _objcBridgeWitnessCache.load(SWIFT_MEMORY_ORDER_CONSUME);
|
|
if (cached.metadata == T) {
|
|
return cached.witness;
|
|
}
|
|
cached.witness = swift_conformsToObjectiveCBridgeableNoCache(T);
|
|
cached.metadata = T;
|
|
_objcBridgeWitnessCache.store(cached, std::memory_order_release);
|
|
return cached.witness;
|
|
}
|
|
|
|
static const _ObjectiveCBridgeableWitnessTable *
|
|
findBridgeWitness(const Metadata *T) {
|
|
// Special case: Memoize the bridge witness for Swift.String.
|
|
// Swift.String is the most heavily used bridge because of the prevalence of
|
|
// string-keyed dictionaries in Obj-C. It's worth burning a few words of static
|
|
// storage to avoid repeatedly looking up this conformance.
|
|
if (T->getKind() == MetadataKind::Struct) {
|
|
auto structDescription = cast<StructMetadata>(T)->Description;
|
|
if (structDescription == &NOMINAL_TYPE_DESCR_SYM(SS)) {
|
|
static auto *Swift_String_ObjectiveCBridgeable = swift_conformsToObjectiveCBridgeableNoCache(T);
|
|
return Swift_String_ObjectiveCBridgeable;
|
|
}
|
|
}
|
|
|
|
auto w = swift_conformsToObjectiveCBridgeable(T);
|
|
if (SWIFT_LIKELY(w))
|
|
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
|
|
// Class and ObjC existential metatypes can be bridged, but metatypes can't
|
|
// directly conform to protocols yet. Use a stand-in conformance for a type
|
|
// that looks like a metatype value if the metatype can be bridged.
|
|
switch (T->getKind()) {
|
|
case MetadataKind::Metatype: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto metaTy = static_cast<const MetatypeMetadata *>(T);
|
|
if (metaTy->InstanceType->isAnyClass())
|
|
return &BRIDGING_CONFORMANCE_SYM;
|
|
#endif
|
|
break;
|
|
}
|
|
case MetadataKind::ExistentialMetatype: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto existentialMetaTy =
|
|
static_cast<const ExistentialMetatypeMetadata *>(T);
|
|
if (existentialMetaTy->isObjC())
|
|
return &BRIDGING_CONFORMANCE_SYM;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// Retrieve the bridged Objective-C type for the given type that
|
|
/// conforms to \c _ObjectiveCBridgeable.
|
|
MetadataResponse
|
|
_getBridgedObjectiveCType(
|
|
MetadataRequest request,
|
|
const Metadata *conformingType,
|
|
const _ObjectiveCBridgeableWitnessTable *wtable)
|
|
{
|
|
// FIXME: Can we directly reference the descriptor somehow?
|
|
const ProtocolConformanceDescriptor *conformance = wtable->getDescription();
|
|
const ProtocolDescriptor *protocol = conformance->getProtocol();
|
|
auto assocTypeRequirement = protocol->getRequirements().begin();
|
|
assert(assocTypeRequirement->Flags.getKind() ==
|
|
ProtocolRequirementFlags::Kind::AssociatedTypeAccessFunction);
|
|
auto mutableWTable = (WitnessTable *)wtable;
|
|
return swift_getAssociatedTypeWitness(
|
|
request, mutableWTable, conformingType,
|
|
protocol->getRequirementBaseDescriptor(),
|
|
assocTypeRequirement);
|
|
}
|
|
|
|
/// Dynamic cast from a class type to a value type that conforms to the
|
|
/// _ObjectiveCBridgeable, first by dynamic casting the object to the
|
|
/// class to which the value type is bridged, and then bridging
|
|
/// from that object to the value type via the witness table.
|
|
///
|
|
/// Caveat: Despite the name, this is also used to bridge pure Swift
|
|
/// classes to Swift value types even when Obj-C is not being used.
|
|
|
|
static DynamicCastResult
|
|
_tryCastFromClassToObjCBridgeable(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType, void *srcObject,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks,
|
|
const _ObjectiveCBridgeableWitnessTable *destBridgeWitness,
|
|
const Metadata *targetBridgeClass)
|
|
{
|
|
|
|
// 2. Allocate a T? to receive the bridge result.
|
|
|
|
// The extra byte is for the tag.
|
|
auto targetSize = destType->getValueWitnesses()->getSize() + 1;
|
|
auto targetAlignMask = destType->getValueWitnesses()->getAlignmentMask();
|
|
|
|
// Object that frees a buffer when it goes out of scope.
|
|
struct FreeBuffer {
|
|
void *Buffer = nullptr;
|
|
size_t size, alignMask;
|
|
FreeBuffer(size_t size, size_t alignMask) :
|
|
size(size), alignMask(alignMask) {}
|
|
|
|
~FreeBuffer() {
|
|
if (Buffer)
|
|
swift_slowDealloc(Buffer, size, alignMask);
|
|
}
|
|
} freeBuffer{targetSize, targetAlignMask};
|
|
|
|
// The extra byte is for the tag on the T?
|
|
const std::size_t inlineValueSize = 3 * sizeof(void*);
|
|
alignas(MaximumAlignment) char inlineBuffer[inlineValueSize + 1];
|
|
void *optDestBuffer;
|
|
if (destType->getValueWitnesses()->getStride() <= inlineValueSize) {
|
|
// Use the inline buffer.
|
|
optDestBuffer = inlineBuffer;
|
|
} else {
|
|
// Allocate a buffer.
|
|
optDestBuffer = swift_slowAlloc(targetSize, targetAlignMask);
|
|
freeBuffer.Buffer = optDestBuffer;
|
|
}
|
|
|
|
// Initialize the buffer as an empty optional.
|
|
destType->vw_storeEnumTagSinglePayload((OpaqueValue *)optDestBuffer,
|
|
1, 1);
|
|
|
|
// 3. Bridge into the T? (Effectively a copy operation.)
|
|
bool success;
|
|
if (mayDeferChecks) {
|
|
destBridgeWitness->forceBridgeFromObjectiveC(
|
|
(HeapObject *)srcObject, (OpaqueValue *)optDestBuffer,
|
|
destType, destType, destBridgeWitness);
|
|
success = true;
|
|
} else {
|
|
success = destBridgeWitness->conditionallyBridgeFromObjectiveC(
|
|
(HeapObject *)srcObject, (OpaqueValue *)optDestBuffer,
|
|
destType, destType, destBridgeWitness);
|
|
}
|
|
|
|
// If we succeeded, then take the value from the temp buffer.
|
|
if (success) {
|
|
destType->vw_initializeWithTake(destLocation, (OpaqueValue *)optDestBuffer);
|
|
// Bridge above is effectively a copy, so overall we're a copy.
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastFromClassToObjCBridgeable(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks)
|
|
{
|
|
// We need the _ObjectiveCBridgeable conformance for the target
|
|
auto destBridgeWitness = findBridgeWitness(destType);
|
|
if (destBridgeWitness == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// 1. Soundness check whether the source object can cast to the
|
|
// type expected by the target.
|
|
|
|
auto targetBridgedClass =
|
|
_getBridgedObjectiveCType(MetadataState::Complete, destType,
|
|
destBridgeWitness).Value;
|
|
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// Note: srcObject can be null here in compatibility mode
|
|
if (nullptr == srcObject
|
|
|| nullptr == swift_dynamicCastUnknownClass(srcObject, targetBridgedClass)) {
|
|
destFailureType = targetBridgedClass;
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
return _tryCastFromClassToObjCBridgeable(
|
|
destLocation, destType,
|
|
srcValue, srcType, srcObject,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess, mayDeferChecks,
|
|
destBridgeWitness, targetBridgedClass);
|
|
}
|
|
|
|
/// Dynamic cast from a value type that conforms to the
|
|
/// _ObjectiveCBridgeable protocol to a class type, first by bridging
|
|
/// the value to its Objective-C object representation and then by
|
|
/// dynamic casting that object to the resulting target type.
|
|
///
|
|
/// Caveat: Despite the name, this is also used to bridge Swift value types
|
|
/// to Swift classes even when Obj-C is not being used.
|
|
static DynamicCastResult
|
|
tryCastFromObjCBridgeableToClass(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks)
|
|
{
|
|
auto srcBridgeWitness = findBridgeWitness(srcType);
|
|
if (srcBridgeWitness == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// Bridge the source value to an object.
|
|
auto srcBridgedObject =
|
|
srcBridgeWitness->bridgeToObjectiveC(srcValue, srcType, srcBridgeWitness);
|
|
|
|
// Dynamic cast the object to the resulting class type.
|
|
if (auto cast = swift_dynamicCastUnknownClass(srcBridgedObject, destType)) {
|
|
*reinterpret_cast<const void **>(destLocation) = cast;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
// We don't need the object anymore.
|
|
swift_unknownObjectRelease(srcBridgedObject);
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/****************************** SwiftValue Boxing *****************************/
|
|
/******************************************************************************/
|
|
|
|
#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
bool swift_unboxFromSwiftValueWithType(OpaqueValue *source,
|
|
OpaqueValue *result,
|
|
const Metadata *destinationType);
|
|
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
bool swift_swiftValueConformsTo(const Metadata *, const Metadata *);
|
|
#endif
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Try unwrapping a source holding an Obj-C SwiftValue container and
|
|
// recursively casting the contents.
|
|
static DynamicCastResult
|
|
tryCastUnwrappingObjCSwiftValueSource(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
id srcObject;
|
|
memcpy(&srcObject, srcValue, sizeof(id));
|
|
auto srcSwiftValue = getAsSwiftValue(srcObject);
|
|
|
|
if (srcSwiftValue == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
const Metadata *srcInnerType;
|
|
const OpaqueValue *srcInnerValue;
|
|
std::tie(srcInnerType, srcInnerValue)
|
|
= getValueFromSwiftValue(srcSwiftValue);
|
|
// Note: We never `take` the contents from a SwiftValue box as
|
|
// it might have other references. Instead, let our caller
|
|
// destroy the reference if necessary.
|
|
return tryCast(
|
|
destLocation, destType,
|
|
const_cast<OpaqueValue *>(srcInnerValue), srcInnerType,
|
|
destFailureType, srcFailureType,
|
|
/*takeOnSuccess=*/ false, mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
#else
|
|
static DynamicCastResult
|
|
tryCastUnwrappingSwiftValueSource(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType->getKind() == MetadataKind::Class);
|
|
|
|
// unboxFromSwiftValueWithType is really just a recursive casting operation...
|
|
if (swift_unboxFromSwiftValueWithType(srcValue, destLocation, destType)) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
/****************************** Class Destination *****************************/
|
|
/******************************************************************************/
|
|
|
|
static DynamicCastResult
|
|
tryCastToSwiftClass(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Class);
|
|
|
|
auto destClassType = cast<ClassMetadata>(destType);
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class: // Swift class => Swift class
|
|
case MetadataKind::ObjCClassWrapper: { // Obj-C class => Swift class
|
|
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// Note: srcObject can be null in compatibility mode.
|
|
if (srcObject == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
if (auto t = swift_dynamicCastClass(srcObject, destClassType)) {
|
|
auto castObject = const_cast<void *>(t);
|
|
*(reinterpret_cast<void **>(destLocation)) = castObject;
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
swift_unknownObjectRetain(castObject);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
} else {
|
|
srcFailureType = srcType;
|
|
destFailureType = destType;
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
case MetadataKind::ForeignClass: // CF class => Swift class
|
|
// Top-level code will "unwrap" to an Obj-C class and try again.
|
|
return DynamicCastResult::Failure;
|
|
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToObjectiveCClass(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::ObjCClassWrapper);
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto destObjCType = cast<ObjCClassWrapperMetadata>(destType);
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class: // Swift class => Obj-C class
|
|
case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class
|
|
case MetadataKind::ForeignClass: { // CF class => Obj-C class
|
|
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If object is null, then we're in the compatibility mode.
|
|
// Earlier cast logic always succeeded `as!` casts of nil
|
|
// class references but failed `as?` and `is`
|
|
if (srcObject == nullptr) {
|
|
if (mayDeferChecks) {
|
|
*reinterpret_cast<const void **>(destLocation) = nullptr;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
auto destObjCClass = destObjCType->Class;
|
|
if (auto resultObject
|
|
= swift_dynamicCastObjCClass(srcObject, destObjCClass)) {
|
|
*reinterpret_cast<const void **>(destLocation) = resultObject;
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
objc_retain((id)const_cast<void *>(resultObject));
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToForeignClass(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
#if SWIFT_OBJC_INTEROP
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::ForeignClass);
|
|
auto destClassType = cast<ForeignClassMetadata>(destType);
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class: // Swift class => CF class
|
|
case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class
|
|
case MetadataKind::ForeignClass: { // CF class => CF class
|
|
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If srcObject is null, then we're in compatibility mode.
|
|
// Earlier cast logic always succeeded `as!` casts of nil
|
|
// class references. Yes, this is very dangerous, which
|
|
// is why we no longer permit it.
|
|
if (srcObject == nullptr) {
|
|
if (mayDeferChecks) {
|
|
*reinterpret_cast<const void **>(destLocation) = nullptr;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
// `as?` and `is` checks always fail on nil sources
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
if (auto resultObject
|
|
= swift_dynamicCastForeignClass(srcObject, destClassType)) {
|
|
*reinterpret_cast<const void **>(destLocation) = resultObject;
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
objc_retain((id)const_cast<void *>(resultObject));
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult tryCastToForeignReferenceType(
|
|
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
|
|
const Metadata *srcType, const Metadata *&destFailureType,
|
|
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks,
|
|
bool prohibitIsolatedConformances) {
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::ForeignReferenceType);
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/***************************** Enum Destination *******************************/
|
|
/******************************************************************************/
|
|
|
|
static DynamicCastResult
|
|
tryCastToEnum(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
// Note: Optional is handled elsewhere
|
|
assert(destType->getKind() == MetadataKind::Enum);
|
|
|
|
// Enum has no special cast support at present.
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** Struct Destination ******************************/
|
|
/******************************************************************************/
|
|
|
|
// internal func _arrayDownCastIndirect<SourceValue, TargetValue>(
|
|
// _ source: UnsafePointer<Array<SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Array<TargetValue>>)
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
void _swift_arrayDownCastIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetValueType);
|
|
|
|
// internal func _arrayDownCastConditionalIndirect<SourceValue, TargetValue>(
|
|
// _ source: UnsafePointer<Array<SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Array<TargetValue>>
|
|
// ) -> Bool
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
bool _swift_arrayDownCastConditionalIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetValueType);
|
|
|
|
// internal func _setDownCastIndirect<SourceValue, TargetValue>(
|
|
// _ source: UnsafePointer<Set<SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Set<TargetValue>>)
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
void _swift_setDownCastIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetValueType,
|
|
const void *sourceValueHashable,
|
|
const void *targetValueHashable);
|
|
|
|
// internal func _setDownCastConditionalIndirect<SourceValue, TargetValue>(
|
|
// _ source: UnsafePointer<Set<SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Set<TargetValue>>
|
|
// ) -> Bool
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
bool _swift_setDownCastConditionalIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetValueType,
|
|
const void *sourceValueHashable,
|
|
const void *targetValueHashable);
|
|
|
|
// internal func _dictionaryDownCastIndirect<SourceKey, SourceValue,
|
|
// TargetKey, TargetValue>(
|
|
// _ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>)
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
void _swift_dictionaryDownCastIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceKeyType,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetKeyType,
|
|
const Metadata *targetValueType,
|
|
const void *sourceKeyHashable,
|
|
const void *targetKeyHashable);
|
|
|
|
// internal func _dictionaryDownCastConditionalIndirect<SourceKey, SourceValue,
|
|
// TargetKey, TargetValue>(
|
|
// _ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
|
|
// _ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>
|
|
// ) -> Bool
|
|
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
|
|
bool _swift_dictionaryDownCastConditionalIndirect(OpaqueValue *destination,
|
|
OpaqueValue *source,
|
|
const Metadata *sourceKeyType,
|
|
const Metadata *sourceValueType,
|
|
const Metadata *targetKeyType,
|
|
const Metadata *targetValueType,
|
|
const void *sourceKeyHashable,
|
|
const void *targetKeyHashable);
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Helper to memoize bridging conformance data for a particular
|
|
// Swift struct type. This is used to speed up the most common
|
|
// ObjC->Swift bridging conversions by eliminating repeated
|
|
// protocol conformance lookups.
|
|
|
|
// Currently used only for String, which may be the only
|
|
// type used often enough to justify the extra static memory.
|
|
struct ObjCBridgeMemo {
|
|
#if !NDEBUG
|
|
// Used in assert build to verify that we always get called with
|
|
// the same destType
|
|
const Metadata *destType;
|
|
#endif
|
|
const _ObjectiveCBridgeableWitnessTable *destBridgeWitness;
|
|
const Metadata *targetBridgedType;
|
|
Class targetBridgedObjCClass;
|
|
swift_once_t fetchWitnessOnce;
|
|
|
|
DynamicCastResult tryBridge(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks)
|
|
{
|
|
struct SetupData {
|
|
const Metadata *destType;
|
|
struct ObjCBridgeMemo *memo;
|
|
} setupData { destType, this };
|
|
|
|
swift_once(&fetchWitnessOnce,
|
|
[](void *data) {
|
|
struct SetupData *setupData = (struct SetupData *)data;
|
|
struct ObjCBridgeMemo *memo = setupData->memo;
|
|
#if !NDEBUG
|
|
memo->destType = setupData->destType;
|
|
#endif
|
|
memo->destBridgeWitness = swift_conformsToObjectiveCBridgeableNoCache(setupData->destType);
|
|
if (memo->destBridgeWitness == nullptr) {
|
|
memo->targetBridgedType = nullptr;
|
|
memo->targetBridgedObjCClass = nullptr;
|
|
} else {
|
|
memo->targetBridgedType = _getBridgedObjectiveCType(
|
|
MetadataState::Complete, setupData->destType, memo->destBridgeWitness).Value;
|
|
assert(memo->targetBridgedType->getKind() == MetadataKind::ObjCClassWrapper);
|
|
memo->targetBridgedObjCClass = memo->targetBridgedType->getObjCClassObject();
|
|
assert(memo->targetBridgedObjCClass != nullptr);
|
|
}
|
|
}, (void *)&setupData);
|
|
|
|
// Check that this always gets called with the same destType.
|
|
assert((destType == this->destType) && "ObjC casting memo used inconsistently");
|
|
|
|
// !! If bridging is not usable, stop here.
|
|
if (targetBridgedObjCClass == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
// Use the dynamic ISA type of the object always (Note that this
|
|
// also implicitly gives us the ObjC type for a CF object.)
|
|
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If srcObject is null, then we're in backwards compatibility mode.
|
|
if (srcObject == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
Class srcObjCType = object_getClass((id)srcObject);
|
|
// Fail if the ObjC object is not a subclass of the bridge class.
|
|
while (srcObjCType != targetBridgedObjCClass) {
|
|
srcObjCType = class_getSuperclass(srcObjCType);
|
|
if (srcObjCType == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
// The ObjC object is an acceptable type, so call the bridge function...
|
|
return _tryCastFromClassToObjCBridgeable(
|
|
destLocation, destType, srcValue, srcType, srcObject,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess, mayDeferChecks,
|
|
destBridgeWitness, targetBridgedType);
|
|
}
|
|
};
|
|
|
|
static const HashableWitnessTable* tryMemoizeNSStringHashableConformance(const Metadata *cls) {
|
|
auto nsString = getNSStringMetadata();
|
|
do {
|
|
if (cls == nsString) {
|
|
return getNSStringHashableConformance();
|
|
}
|
|
cls = _swift_class_getSuperclass(cls);
|
|
} while (cls != nullptr);
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
static DynamicCastResult
|
|
tryCastToAnyHashable(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
assert(cast<StructMetadata>(destType)->Description
|
|
== &STRUCT_TYPE_DESCR_SYM(s11AnyHashable));
|
|
|
|
const HashableWitnessTable *hashableConformance = nullptr;
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Existential: {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
case MetadataKind::ForeignClass: // CF -> String
|
|
case MetadataKind::ObjCClassWrapper: { // Obj-C -> String
|
|
#if SWIFT_OBJC_INTEROP
|
|
hashableConformance = tryMemoizeNSStringHashableConformance(srcType);
|
|
#endif
|
|
// If no Obj-C interop, just fall through to the general case.
|
|
break;
|
|
}
|
|
case MetadataKind::Optional: {
|
|
// FIXME: https://github.com/apple/swift/issues/51550
|
|
// Until the interactions between AnyHashable and Optional is fixed, we
|
|
// avoid directly injecting Optionals. In particular, this allows casts
|
|
// from [String?:String] to [AnyHashable:Any] to work the way people
|
|
// expect. Otherwise, the resulting dictionary can only be indexed with an
|
|
// explicit Optional<String>, not a plain String.
|
|
// After fixing the issue, we can consider dropping this special
|
|
// case entirely.
|
|
|
|
// !!!! This breaks compatibility with compiler-optimized casts
|
|
// (which just inject) and violates the Casting Spec. It just preserves
|
|
// the behavior of the older casting code until we can clean things up.
|
|
auto srcInnerType = cast<EnumMetadata>(srcType)->getGenericArgs()[0];
|
|
unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload(
|
|
srcValue, /*emptyCases=*/1);
|
|
auto nonNil = (sourceEnumCase == 0);
|
|
if (nonNil) {
|
|
return DynamicCastResult::Failure; // Our caller will unwrap the optional and try again
|
|
}
|
|
// Else Optional is nil -- the general case below will inject it
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
// General case: If it conforms to Hashable, we cast it
|
|
if (hashableConformance == nullptr) {
|
|
hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
|
|
swift_conformsToProtocolCommon(srcType, &HashableProtocolDescriptor)
|
|
);
|
|
}
|
|
if (hashableConformance) {
|
|
_swift_convertToAnyHashableIndirect(srcValue, destLocation,
|
|
srcType, hashableConformance);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToArray(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
assert(cast<StructMetadata>(destType)->Description
|
|
== &NOMINAL_TYPE_DESCR_SYM(Sa));
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Struct: { // Struct -> Array
|
|
const auto srcStructType = cast<StructMetadata>(srcType);
|
|
if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(Sa)) { // Array -> Array
|
|
auto sourceArgs = srcType->getGenericArgs();
|
|
auto destArgs = destType->getGenericArgs();
|
|
if (mayDeferChecks) {
|
|
_swift_arrayDownCastIndirect(
|
|
srcValue, destLocation, sourceArgs[0], destArgs[0]);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
auto result = _swift_arrayDownCastConditionalIndirect(
|
|
srcValue, destLocation, sourceArgs[0], destArgs[0]);
|
|
if (result) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToDictionary(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
assert(cast<StructMetadata>(destType)->Description
|
|
== &NOMINAL_TYPE_DESCR_SYM(SD));
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Struct: { // Struct -> Dictionary
|
|
const auto srcStructType = cast<StructMetadata>(srcType);
|
|
if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(SD)) { // Dictionary -> Dictionary
|
|
auto sourceArgs = srcType->getGenericArgs();
|
|
auto destArgs = destType->getGenericArgs();
|
|
if (mayDeferChecks) {
|
|
_swift_dictionaryDownCastIndirect(
|
|
srcValue, destLocation, sourceArgs[0], sourceArgs[1],
|
|
destArgs[0], destArgs[1], sourceArgs[2], destArgs[2]);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
auto result = _swift_dictionaryDownCastConditionalIndirect(
|
|
srcValue, destLocation, sourceArgs[0], sourceArgs[1],
|
|
destArgs[0], destArgs[1], sourceArgs[2], destArgs[2]);
|
|
if (result) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToSet(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
assert(cast<StructMetadata>(destType)->Description
|
|
== &NOMINAL_TYPE_DESCR_SYM(Sh));
|
|
|
|
switch (srcType->getKind()) {
|
|
|
|
case MetadataKind::Struct: { // Struct -> Set
|
|
const auto srcStructType = cast<StructMetadata>(srcType);
|
|
if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(Sh)) { // Set -> Set
|
|
auto sourceArgs = srcType->getGenericArgs();
|
|
auto destArgs = destType->getGenericArgs();
|
|
if (mayDeferChecks) {
|
|
_swift_setDownCastIndirect(srcValue, destLocation,
|
|
sourceArgs[0], destArgs[0], sourceArgs[1], destArgs[1]);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
auto result = _swift_setDownCastConditionalIndirect(
|
|
srcValue, destLocation,
|
|
sourceArgs[0], destArgs[0],
|
|
sourceArgs[1], destArgs[1]);
|
|
if (result) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToString(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
assert(cast<StructMetadata>(destType)->Description
|
|
== &NOMINAL_TYPE_DESCR_SYM(SS));
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::ForeignClass: // CF -> String
|
|
case MetadataKind::ObjCClassWrapper: { // Obj-C -> String
|
|
#if SWIFT_OBJC_INTEROP
|
|
static ObjCBridgeMemo memo;
|
|
return memo.tryBridge(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess, mayDeferChecks);
|
|
#else
|
|
SWIFT_FALLTHROUGH;
|
|
#endif
|
|
}
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToStruct(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Struct);
|
|
|
|
// There is no special cast handling at present for general Struct types.
|
|
|
|
// Special logic for AnyHashable, Set, Dictionary, Array, and String
|
|
// is broken out above. See also selectCasterForDest() for the
|
|
// logic that chooses one of these functions.
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*************************** Optional Destination *****************************/
|
|
/******************************************************************************/
|
|
|
|
static DynamicCastResult
|
|
tryCastToOptional(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Optional);
|
|
|
|
// Nothing to do for the basic casting operation.
|
|
// Unwrapping is handled by top-level tryCast with assistance
|
|
// from utility functions below.
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// The nil value `T?.none` can be cast to any optional type.
|
|
// When the unwrapper sees a source value that is nil, it calls
|
|
// tryCastFromNil() to try to set the target optional to nil.
|
|
//
|
|
// This is complicated by the desire to preserve the nesting
|
|
// as far as possible. For example, we would like:
|
|
// Int?.none => Int??.some(.none)
|
|
// Int??.none => Any????.some(.some(.none))
|
|
// Of course, if the target is shallower than the source,
|
|
// then we have to just set the outermost optional to nil.
|
|
|
|
// This helper sets a nested optional to nil at a requested level:
|
|
static void
|
|
initializeToNilAtDepth(OpaqueValue *destLocation, const Metadata *destType, int depth) {
|
|
assert(destType->getKind() == MetadataKind::Optional);
|
|
auto destInnerType = cast<EnumMetadata>(destType)->getGenericArgs()[0];
|
|
if (depth > 0) {
|
|
initializeToNilAtDepth(destLocation, destInnerType, depth - 1);
|
|
// Set .some at each level as we unwind
|
|
destInnerType->vw_storeEnumTagSinglePayload(
|
|
destLocation, 0, 1);
|
|
} else {
|
|
// Set .none at the lowest level
|
|
destInnerType->vw_storeEnumTagSinglePayload(
|
|
destLocation, 1, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
copyNilPreservingDepth(OpaqueValue *destLocation, const Metadata *destType, const Metadata *srcType)
|
|
{
|
|
assert(srcType->getKind() == MetadataKind::Optional);
|
|
assert(destType->getKind() == MetadataKind::Optional);
|
|
|
|
// Measure how deep the source nil is: Is it Int?.none or Int??.none or ...
|
|
auto srcInnerType = cast<EnumMetadata>(srcType)->getGenericArgs()[0];
|
|
int srcDepth = 1;
|
|
while (srcInnerType->getKind() == MetadataKind::Optional) {
|
|
srcInnerType = cast<EnumMetadata>(srcInnerType)->getGenericArgs()[0];
|
|
srcDepth += 1;
|
|
}
|
|
|
|
// Measure how deep the destination optional is
|
|
auto destInnerType = cast<EnumMetadata>(destType)->getGenericArgs()[0];
|
|
int destDepth = 1;
|
|
while (destInnerType->getKind() == MetadataKind::Optional) {
|
|
destInnerType = cast<EnumMetadata>(destInnerType)->getGenericArgs()[0];
|
|
destDepth += 1;
|
|
}
|
|
|
|
// Recursively set the destination to .some(.some(... .some(.none)))
|
|
auto targetDepth = std::max(destDepth - srcDepth, 0);
|
|
initializeToNilAtDepth(destLocation, destType, targetDepth);
|
|
}
|
|
|
|
// Try unwrapping both source and dest optionals together.
|
|
// If the source is nil, then cast that to the destination.
|
|
static DynamicCastResult
|
|
tryCastUnwrappingOptionalBoth(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(destType->getKind() == MetadataKind::Optional);
|
|
assert(srcType->getKind() == MetadataKind::Optional);
|
|
|
|
auto srcInnerType = cast<EnumMetadata>(srcType)->getGenericArgs()[0];
|
|
unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload(
|
|
srcValue, /*emptyCases=*/1);
|
|
auto sourceIsNil = (sourceEnumCase != 0);
|
|
if (sourceIsNil) {
|
|
if (runtime::bincompat::useLegacyOptionalNilInjectionInCasting()) {
|
|
auto destInnerType = cast<EnumMetadata>(destType)->getGenericArgs()[0];
|
|
// Set .none at the outer level
|
|
destInnerType->vw_storeEnumTagSinglePayload(destLocation, 1, 1);
|
|
} else {
|
|
copyNilPreservingDepth(destLocation, destType, srcType);
|
|
}
|
|
return DynamicCastResult::SuccessViaCopy; // nil was essentially copied to dest
|
|
} else {
|
|
auto destEnumType = cast<EnumMetadata>(destType);
|
|
const Metadata *destInnerType = destEnumType->getGenericArgs()[0];
|
|
auto destInnerLocation = destLocation; // Single-payload enum layout
|
|
auto subcastResult = tryCast(
|
|
destInnerLocation, destInnerType, srcValue, srcInnerType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
destInnerType->vw_storeEnumTagSinglePayload(
|
|
destLocation, /*case*/ 0, /*emptyCases*/ 1);
|
|
}
|
|
return subcastResult;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// Try unwrapping just the destination optional.
|
|
// Note we do this even if both src and dest are optional.
|
|
// For example, Int -> Any? requires unwrapping the destination
|
|
// in order to inject the Int into the existential.
|
|
static DynamicCastResult
|
|
tryCastUnwrappingOptionalDestination(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(destType->getKind() == MetadataKind::Optional);
|
|
|
|
auto destEnumType = cast<EnumMetadata>(destType);
|
|
const Metadata *destInnerType = destEnumType->getGenericArgs()[0];
|
|
auto destInnerLocation = destLocation; // Single-payload enum layout
|
|
auto subcastResult = tryCast(
|
|
destInnerLocation, destInnerType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
destInnerType->vw_storeEnumTagSinglePayload(
|
|
destLocation, /*case*/ 0, /*emptyCases*/ 1);
|
|
}
|
|
return subcastResult;
|
|
}
|
|
|
|
// Try unwrapping just the source optional.
|
|
// Note we do this even if both target and dest are optional.
|
|
// For example, this is used when extracting the contents of
|
|
// an Optional<Any>.
|
|
static DynamicCastResult
|
|
tryCastUnwrappingOptionalSource(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType->getKind() == MetadataKind::Optional);
|
|
|
|
auto srcInnerType = cast<EnumMetadata>(srcType)->getGenericArgs()[0];
|
|
unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload(
|
|
srcValue, /*emptyCases=*/1);
|
|
auto nonNil = (sourceEnumCase == 0);
|
|
if (nonNil) {
|
|
// Recurse with unwrapped source
|
|
return tryCast(destLocation, destType, srcValue, srcInnerType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/***************************** Tuple Destination ******************************/
|
|
/******************************************************************************/
|
|
|
|
// The only thing that can be legally cast to a tuple is another tuple.
|
|
// Most of the logic below is required to handle element-wise casts of
|
|
// tuples that are compatible but not of the same type.
|
|
|
|
static DynamicCastResult
|
|
tryCastToTuple(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Tuple);
|
|
const auto destTupleType = cast<TupleTypeMetadata>(destType);
|
|
|
|
srcFailureType = srcType;
|
|
destFailureType = destType;
|
|
|
|
// We cannot cast non-tuple data to a tuple
|
|
if (srcType->getKind() != MetadataKind::Tuple) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
const auto srcTupleType = cast<TupleTypeMetadata>(srcType);
|
|
|
|
// Tuples must have same number of elements
|
|
if (srcTupleType->NumElements != destTupleType->NumElements) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// Common labels must match
|
|
const char *srcLabels = srcTupleType->Labels;
|
|
const char *destLabels = destTupleType->Labels;
|
|
while (srcLabels != nullptr
|
|
&& destLabels != nullptr
|
|
&& srcLabels != destLabels)
|
|
{
|
|
const char *srcSpace = strchr(srcLabels, ' ');
|
|
const char *destSpace = strchr(destLabels, ' ');
|
|
|
|
// If we've reached the end of either label string, we're done.
|
|
if (srcSpace == nullptr || destSpace == nullptr) {
|
|
break;
|
|
}
|
|
|
|
// If both labels are non-empty, and the labels mismatch, we fail.
|
|
if (srcSpace != srcLabels && destSpace != destLabels) {
|
|
unsigned srcLen = srcSpace - srcLabels;
|
|
unsigned destLen = destSpace - destLabels;
|
|
if (srcLen != destLen ||
|
|
strncmp(srcLabels, destLabels, srcLen) != 0)
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
srcLabels = srcSpace + 1;
|
|
destLabels = destSpace + 1;
|
|
}
|
|
|
|
// Compare the element types to see if they all match.
|
|
bool typesMatch = true;
|
|
auto numElements = srcTupleType->NumElements;
|
|
for (unsigned i = 0; typesMatch && i != numElements; ++i) {
|
|
if (srcTupleType->getElement(i).Type != destTupleType->getElement(i).Type) {
|
|
typesMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (typesMatch) {
|
|
// The actual element types are identical, so we can use the
|
|
// fast value-witness machinery for the whole tuple.
|
|
if (takeOnSuccess) {
|
|
srcType->vw_initializeWithTake(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
srcType->vw_initializeWithCopy(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
} else {
|
|
// Slow path casts each item separately.
|
|
for (unsigned j = 0, n = srcTupleType->NumElements; j != n; ++j) {
|
|
const auto &srcElt = srcTupleType->getElement(j);
|
|
const auto &destElt = destTupleType->getElement(j);
|
|
auto subcastResult = tryCast(destElt.findIn(destLocation), destElt.Type,
|
|
srcElt.findIn(srcValue), srcElt.Type,
|
|
destFailureType, srcFailureType,
|
|
false, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (subcastResult == DynamicCastResult::Failure) {
|
|
for (unsigned k = 0; k != j; ++k) {
|
|
const auto &elt = destTupleType->getElement(k);
|
|
elt.Type->vw_destroy(elt.findIn(destLocation));
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
// We succeeded by casting each item.
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** Function Destination ****************************/
|
|
/******************************************************************************/
|
|
|
|
// The only thing that can be legally cast to a function is another function.
|
|
|
|
static DynamicCastResult
|
|
tryCastToFunction(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Function);
|
|
const auto destFuncType = cast<FunctionTypeMetadata>(destType);
|
|
|
|
// Function casts succeed on exact matches, or if the target type is
|
|
// throwier than the source type.
|
|
//
|
|
// TODO: We could also allow ABI-compatible variance, such as casting
|
|
// a dynamic Base -> Derived to Derived -> Base. We wouldn't be able to
|
|
// perform a dynamic cast that required any ABI adjustment without a JIT
|
|
// though.
|
|
|
|
if (srcType->getKind() != MetadataKind::Function) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
auto srcFuncType = cast<FunctionTypeMetadata>(srcType);
|
|
|
|
// Check that argument counts and convention match (but ignore
|
|
// "throws" for now).
|
|
if (srcFuncType->Flags.withThrows(false)
|
|
!= destFuncType->Flags.withThrows(false)) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// If the target type can't throw, neither can the source.
|
|
if (srcFuncType->isThrowing() && !destFuncType->isThrowing())
|
|
return DynamicCastResult::Failure;
|
|
|
|
// The result and argument types must match.
|
|
if (srcFuncType->ResultType != destFuncType->ResultType)
|
|
return DynamicCastResult::Failure;
|
|
if (srcFuncType->getNumParameters() != destFuncType->getNumParameters())
|
|
return DynamicCastResult::Failure;
|
|
if (srcFuncType->hasParameterFlags() != destFuncType->hasParameterFlags())
|
|
return DynamicCastResult::Failure;
|
|
for (unsigned i = 0, e = srcFuncType->getNumParameters(); i < e; ++i) {
|
|
if (srcFuncType->getParameter(i) != destFuncType->getParameter(i) ||
|
|
srcFuncType->getParameterFlags(i) != destFuncType->getParameterFlags(i))
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// Everything matches, so we can take/copy the function reference.
|
|
if (takeOnSuccess) {
|
|
srcType->vw_initializeWithTake(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
srcType->vw_initializeWithCopy(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/************************** Existential Destination ***************************/
|
|
/******************************************************************************/
|
|
|
|
/// Check whether a type conforms to the given protocols, filling in a
|
|
/// list of conformances.
|
|
static bool _conformsToProtocols(const OpaqueValue *value,
|
|
const Metadata *type,
|
|
const ExistentialTypeMetadata *existentialType,
|
|
const WitnessTable **conformances,
|
|
bool prohibitIsolatedConformances) {
|
|
if (auto *superclass = existentialType->getSuperclassConstraint()) {
|
|
if (!swift_dynamicCastMetatype(type, superclass))
|
|
return false;
|
|
}
|
|
|
|
if (existentialType->isClassBounded()) {
|
|
if (!Metadata::isAnyKindOfClass(type->getKind()))
|
|
return false;
|
|
}
|
|
|
|
for (auto protocol : existentialType->getProtocols()) {
|
|
if (!swift::_conformsToProtocolInContext(
|
|
value, type, protocol, conformances, prohibitIsolatedConformances))
|
|
return false;
|
|
if (conformances != nullptr && protocol.needsWitnessTable()) {
|
|
assert(*conformances != nullptr);
|
|
++conformances;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Cast to unconstrained `Any`
|
|
static DynamicCastResult
|
|
tryCastToUnconstrainedOpaqueExistential(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Existential);
|
|
assert(cast<ExistentialTypeMetadata>(destType)->getRepresentation()
|
|
== ExistentialTypeRepresentation::Opaque);
|
|
auto destExistential
|
|
= reinterpret_cast<OpaqueExistentialContainer *>(destLocation);
|
|
|
|
// Fill in the type and value.
|
|
destExistential->Type = srcType;
|
|
auto *destBox = srcType->allocateBoxForExistentialIn(&destExistential->Buffer);
|
|
if (takeOnSuccess) {
|
|
srcType->vw_initializeWithTake(destBox, srcValue);
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
srcType->vw_initializeWithCopy(destBox, srcValue);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
|
|
// Cast to constrained `Any`
|
|
static DynamicCastResult
|
|
tryCastToConstrainedOpaqueExistential(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Existential);
|
|
auto destExistentialType = cast<ExistentialTypeMetadata>(destType);
|
|
assert(destExistentialType->getRepresentation()
|
|
== ExistentialTypeRepresentation::Opaque);
|
|
auto destExistential
|
|
= reinterpret_cast<OpaqueExistentialContainer *>(destLocation);
|
|
|
|
// Check for protocol conformances and fill in the witness tables.
|
|
// TODO (rdar://17033499) If the source is an existential, we should
|
|
// be able to compare the protocol constraints more efficiently than this.
|
|
if (_conformsToProtocols(srcValue, srcType, destExistentialType,
|
|
destExistential->getWitnessTables(),
|
|
prohibitIsolatedConformances)) {
|
|
return tryCastToUnconstrainedOpaqueExistential(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
} else {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToClassExistential(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Existential);
|
|
auto destExistentialType = cast<ExistentialTypeMetadata>(destType);
|
|
assert(destExistentialType->getRepresentation()
|
|
== ExistentialTypeRepresentation::Class);
|
|
auto destExistentialLocation
|
|
= reinterpret_cast<ClassExistentialContainer *>(destLocation);
|
|
|
|
MetadataKind srcKind = srcType->getKind();
|
|
switch (srcKind) {
|
|
|
|
case MetadataKind::Metatype: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Get an object reference to the metatype and try fitting that into
|
|
// the existential...
|
|
auto metatypePtr = reinterpret_cast<const Metadata **>(srcValue);
|
|
auto metatype = *metatypePtr;
|
|
if (auto tmp = swift_dynamicCastMetatypeToObjectConditional(metatype)) {
|
|
auto value = reinterpret_cast<OpaqueValue *>(&tmp);
|
|
auto type = reinterpret_cast<const Metadata *>(tmp);
|
|
if (_conformsToProtocols(value, type, destExistentialType,
|
|
destExistentialLocation->getWitnessTables(),
|
|
prohibitIsolatedConformances)) {
|
|
auto object = *(reinterpret_cast<HeapObject **>(value));
|
|
destExistentialLocation->Value = object;
|
|
if (takeOnSuccess) {
|
|
// We copied the pointer without retain, so the source is no
|
|
// longer valid...
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
swift_unknownObjectRetain(object);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
} else {
|
|
// We didn't assign to destination, so the source reference
|
|
// is still valid and the reference count is still correct.
|
|
}
|
|
}
|
|
#endif
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
case MetadataKind::ObjCClassWrapper:
|
|
#if SWIFT_OBJC_INTEROP
|
|
id srcObject;
|
|
memcpy(&srcObject, srcValue, sizeof(id));
|
|
if (!runtime::bincompat::useLegacySwiftValueUnboxingInCasting()) {
|
|
if (getAsSwiftValue(srcObject) != nullptr) {
|
|
// Do not directly cast a `__SwiftValue` box
|
|
// Return failure so our caller will unwrap and try again
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
#endif
|
|
SWIFT_FALLTHROUGH;
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ForeignClass: {
|
|
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If srcObject is null, then we're in compatibility mode.
|
|
// Earlier cast logic always succeeded `as!` casts of nil
|
|
// class references:
|
|
if (srcObject == nullptr) {
|
|
if (mayDeferChecks) {
|
|
*reinterpret_cast<const void **>(destLocation) = nullptr;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
} else {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
if (_conformsToProtocols(srcValue, srcType,
|
|
destExistentialType,
|
|
destExistentialLocation->getWitnessTables(),
|
|
prohibitIsolatedConformances)) {
|
|
destExistentialLocation->Value = srcObject;
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
swift_unknownObjectRetain(srcObject);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// SwiftValue boxing is a failsafe that we only want to invoke
|
|
// after other approaches have failed. This is why it's not
|
|
// integrated into tryCastToClassExistential() above.
|
|
static DynamicCastResult
|
|
tryCastToClassExistentialViaSwiftValue(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Existential);
|
|
auto destExistentialType = cast<ExistentialTypeMetadata>(destType);
|
|
assert(destExistentialType->getRepresentation()
|
|
== ExistentialTypeRepresentation::Class);
|
|
auto destExistentialLocation
|
|
= reinterpret_cast<ClassExistentialContainer *>(destLocation);
|
|
|
|
switch (srcType->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
// Class references always go directly into
|
|
// class existentials; it makes no sense to wrap them.
|
|
return DynamicCastResult::Failure;
|
|
|
|
case MetadataKind::Metatype: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto metatypePtr = reinterpret_cast<const Metadata **>(srcValue);
|
|
auto metatype = *metatypePtr;
|
|
switch (metatype->getKind()) {
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass:
|
|
// Exclude class metatypes on Darwin, since those are object types and can
|
|
// be stored directly.
|
|
return DynamicCastResult::Failure;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
// Non-class metatypes are never objects, and
|
|
// metatypes on non-Darwin are never objects, so
|
|
// fall through to box those.
|
|
SWIFT_FALLTHROUGH;
|
|
}
|
|
|
|
default: {
|
|
// We can always box when the destination is a simple
|
|
// (unconstrained) `AnyObject`.
|
|
if (destExistentialType->NumProtocols != 0) {
|
|
// But if there are constraints...
|
|
if (!runtime::bincompat::useLegacyObjCBoxingInCasting()) {
|
|
// ... never box if we're not supporting legacy semantics.
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
// Legacy behavior: We used to permit casts to a constrained (existential)
|
|
// type if the resulting `__SwiftValue` box conformed to the target type.
|
|
// This is no longer supported, since it caused `x is NSCopying` to be
|
|
// true even when x does not in fact implement the requirements of
|
|
// `NSCopying`.
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (!findSwiftValueConformances(
|
|
destExistentialType, destExistentialLocation->getWitnessTables())) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
#else
|
|
if (!swift_swiftValueConformsTo(destType, destType)) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
auto object = bridgeAnythingToSwiftValueObject(
|
|
srcValue, srcType, takeOnSuccess);
|
|
destExistentialLocation->Value = object;
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
# else
|
|
// Note: Code below works correctly on both Obj-C and non-Obj-C platforms,
|
|
// but the code above is slightly faster on Obj-C platforms.
|
|
auto object = _bridgeAnythingToObjectiveC(srcValue, srcType);
|
|
destExistentialLocation->Value = object;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastToErrorExistential(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Existential);
|
|
auto destExistentialType = cast<ExistentialTypeMetadata>(destType);
|
|
assert(destExistentialType->getRepresentation()
|
|
== ExistentialTypeRepresentation::Error);
|
|
auto destBoxAddr = reinterpret_cast<SwiftError **>(destLocation);
|
|
|
|
MetadataKind srcKind = srcType->getKind();
|
|
switch (srcKind) {
|
|
case MetadataKind::ForeignClass: // CF object => Error
|
|
case MetadataKind::ObjCClassWrapper: // Obj-C object => Error
|
|
case MetadataKind::Struct: // Struct => Error
|
|
case MetadataKind::Enum: // Enum => Error
|
|
case MetadataKind::Class: { // Class => Error
|
|
assert(destExistentialType->NumProtocols == 1);
|
|
const WitnessTable *errorWitness;
|
|
if (_conformsToProtocols(
|
|
srcValue, srcType, destExistentialType, &errorWitness,
|
|
prohibitIsolatedConformances)) {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// If it already holds an NSError, just use that.
|
|
if (auto embedded = getErrorEmbeddedNSErrorIndirect(
|
|
srcValue, srcType, errorWitness)) {
|
|
*destBoxAddr = reinterpret_cast<SwiftError *>(embedded);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
#endif
|
|
|
|
BoxPair destBox = swift_allocError(
|
|
srcType, errorWitness, srcValue, takeOnSuccess);
|
|
*destBoxAddr = reinterpret_cast<SwiftError *>(destBox.object);
|
|
if (takeOnSuccess) {
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastUnwrappingExistentialSource(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(srcType->getKind() == MetadataKind::Existential);
|
|
|
|
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
|
|
|
// Unpack the existential content
|
|
const Metadata *srcInnerType;
|
|
OpaqueValue *srcInnerValue;
|
|
switch (srcExistentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Class: {
|
|
auto classContainer
|
|
= reinterpret_cast<ClassExistentialContainer *>(srcValue);
|
|
srcInnerType = swift_getObjectType((HeapObject *)classContainer->Value);
|
|
srcInnerValue = reinterpret_cast<OpaqueValue *>(&classContainer->Value);
|
|
break;
|
|
}
|
|
case ExistentialTypeRepresentation::Opaque: {
|
|
auto opaqueContainer
|
|
= reinterpret_cast<OpaqueExistentialContainer*>(srcValue);
|
|
srcInnerType = opaqueContainer->Type;
|
|
srcInnerValue = srcExistentialType->projectValue(srcValue);
|
|
break;
|
|
}
|
|
case ExistentialTypeRepresentation::Error: {
|
|
const SwiftError *errorBox
|
|
= *reinterpret_cast<const SwiftError * const *>(srcValue);
|
|
auto srcErrorValue
|
|
= errorBox->isPureNSError() ? srcValue : errorBox->getValue();
|
|
srcInnerType = errorBox->getType();
|
|
srcInnerValue = const_cast<OpaqueValue *>(srcErrorValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
srcFailureType = srcInnerType;
|
|
return tryCast(destLocation, destType,
|
|
srcInnerValue, srcInnerType,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess && (srcInnerValue == srcValue),
|
|
mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
|
|
static DynamicCastResult tryCastUnwrappingExtendedExistentialSource(
|
|
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
|
|
const Metadata *srcType, const Metadata *&destFailureType,
|
|
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks,
|
|
bool prohibitIsolatedConformances) {
|
|
assert(srcType != destType);
|
|
assert(srcType->getKind() == MetadataKind::ExtendedExistential);
|
|
|
|
auto srcExistentialType = cast<ExtendedExistentialTypeMetadata>(srcType);
|
|
|
|
// Unpack the existential content
|
|
const Metadata *srcInnerType = nullptr;
|
|
OpaqueValue *srcInnerValue = nullptr;
|
|
switch (srcExistentialType->Shape->Flags.getSpecialKind()) {
|
|
case ExtendedExistentialTypeShape::SpecialKind::None: {
|
|
auto opaqueContainer =
|
|
reinterpret_cast<OpaqueExistentialContainer *>(srcValue);
|
|
srcInnerType = opaqueContainer->Type;
|
|
srcInnerValue = const_cast<OpaqueValue *>(opaqueContainer->projectValue());
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::Class: {
|
|
auto classContainer =
|
|
reinterpret_cast<ClassExistentialContainer *>(srcValue);
|
|
srcInnerType = swift_getObjectType((HeapObject *)classContainer->Value);
|
|
srcInnerValue = reinterpret_cast<OpaqueValue *>(&classContainer->Value);
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
|
|
auto srcExistentialContainer =
|
|
reinterpret_cast<ExistentialMetatypeContainer *>(srcValue);
|
|
srcInnerType = swift_getMetatypeMetadata(srcExistentialContainer->Value);
|
|
srcInnerValue = reinterpret_cast<OpaqueValue *>(&srcExistentialContainer->Value);
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout: {
|
|
swift_unreachable("Explicit layout not yet implemented");
|
|
break;
|
|
}
|
|
}
|
|
|
|
srcFailureType = srcInnerType;
|
|
return tryCast(destLocation, destType, srcInnerValue, srcInnerType,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess && (srcInnerValue == srcValue), mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
}
|
|
|
|
static DynamicCastResult
|
|
tryCastUnwrappingExistentialMetatypeSource(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(srcType->getKind() == MetadataKind::ExistentialMetatype);
|
|
|
|
auto srcExistentialContainer = reinterpret_cast<ExistentialMetatypeContainer *>(srcValue);
|
|
auto srcInnerValue = reinterpret_cast<OpaqueValue *>(&srcExistentialContainer->Value);
|
|
assert((const void *)srcInnerValue == (const void *)srcValue);
|
|
auto srcInnerValueAsType = srcExistentialContainer->Value;
|
|
const Metadata *srcInnerType = swift_getMetatypeMetadata(srcInnerValueAsType);
|
|
srcFailureType = srcInnerType;
|
|
return tryCast(destLocation, destType,
|
|
srcInnerValue, srcInnerType,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess && (srcInnerValue == srcValue),
|
|
mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
|
|
|
|
static DynamicCastResult tryCastToExtendedExistential(
|
|
OpaqueValue *destLocation, const Metadata *destType, OpaqueValue *srcValue,
|
|
const Metadata *srcType, const Metadata *&destFailureType,
|
|
const Metadata *&srcFailureType, bool takeOnSuccess, bool mayDeferChecks,
|
|
bool prohibitIsolatedConformances) {
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::ExtendedExistential);
|
|
|
|
auto destExistentialType = cast<ExtendedExistentialTypeMetadata>(destType);
|
|
auto *destExistentialShape = destExistentialType->Shape;
|
|
const unsigned shapeArgumentCount =
|
|
destExistentialShape->getGenSigArgumentLayoutSizeInWords();
|
|
const Metadata *selfType = srcType;
|
|
|
|
// If we have a type expression to look into, unwrap as much metatype
|
|
// structure as possible so we can reach the type metadata for the 'Self'
|
|
// parameter.
|
|
if (destExistentialShape->Flags.hasTypeExpression()) {
|
|
Demangler dem;
|
|
auto *node = dem.demangleType(destExistentialShape->getTypeExpression()->name.get());
|
|
if (!node)
|
|
return DynamicCastResult::Failure;
|
|
|
|
while (node->getKind() == Demangle::Node::Kind::Type &&
|
|
node->getNumChildren() &&
|
|
node->getChild(0)->getKind() == Demangle::Node::Kind::Metatype &&
|
|
node->getChild(0)->getNumChildren()) {
|
|
auto *metatypeMetadata = dyn_cast<MetatypeMetadata>(selfType);
|
|
if (!metatypeMetadata)
|
|
return DynamicCastResult::Failure;
|
|
|
|
selfType = metatypeMetadata->InstanceType;
|
|
node = node->getChild(0)->getChild(0);
|
|
}
|
|
|
|
// Make sure the thing we've pulled out at the end is a dependent
|
|
// generic parameter.
|
|
if (!(node->getKind() == Demangle::Node::Kind::Type &&
|
|
node->getNumChildren() &&
|
|
node->getChild(0)->getKind() ==
|
|
Demangle::Node::Kind::DependentGenericParamType))
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
llvm::SmallVector<const void *, 4> allGenericArgsVec;
|
|
llvm::SmallVector<const void *, 4> witnessTables;
|
|
{
|
|
// Line up the arguments to the requirement signature.
|
|
auto genArgs = destExistentialType->getGeneralizationArguments();
|
|
allGenericArgsVec.append(genArgs, genArgs + shapeArgumentCount);
|
|
// Tack on the `Self` argument.
|
|
allGenericArgsVec.push_back((const void *)selfType);
|
|
|
|
SubstGenericParametersFromMetadata substitutions(destExistentialShape,
|
|
allGenericArgsVec.data());
|
|
// Verify the requirements in the requirement signature against the
|
|
// arguments from the source value.
|
|
ConformanceExecutionContext context;
|
|
auto requirementSig = destExistentialShape->getRequirementSignature();
|
|
auto error = swift::_checkGenericRequirements(
|
|
requirementSig.getParams(),
|
|
requirementSig.getRequirements(),
|
|
witnessTables,
|
|
[&substitutions](unsigned depth, unsigned index) {
|
|
return substitutions.getMetadata(depth, index).Ptr;
|
|
},
|
|
[&substitutions](unsigned fullOrdinal, unsigned keyOrdinal) {
|
|
return substitutions.getMetadataKeyArgOrdinal(keyOrdinal).Ptr;
|
|
},
|
|
[](const Metadata *type, unsigned index) -> const WitnessTable * {
|
|
swift_unreachable("Resolution of witness tables is not supported");
|
|
},
|
|
&context);
|
|
if (error)
|
|
return DynamicCastResult::Failure;
|
|
|
|
if (prohibitIsolatedConformances &&
|
|
context.globalActorIsolationType)
|
|
return DynamicCastResult::Failure;
|
|
|
|
if (!swift_isInConformanceExecutionContext(selfType, &context))
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
OpaqueValue *destBox = nullptr;
|
|
const WitnessTable **destWitnesses = nullptr;
|
|
switch (destExistentialShape->Flags.getSpecialKind()) {
|
|
case ExtendedExistentialTypeShape::SpecialKind::None: {
|
|
auto destExistential =
|
|
reinterpret_cast<OpaqueExistentialContainer *>(destLocation);
|
|
|
|
// Allocate a box and fill in the type information.
|
|
destExistential->Type = srcType;
|
|
destBox = srcType->allocateBoxForExistentialIn(&destExistential->Buffer);
|
|
destWitnesses = destExistential->getWitnessTables();
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::Class: {
|
|
auto destExistential =
|
|
reinterpret_cast<ClassExistentialContainer *>(destLocation);
|
|
destBox = reinterpret_cast<OpaqueValue *>(&destExistential->Value);
|
|
destWitnesses = destExistential->getWitnessTables();
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::Metatype: {
|
|
auto destExistential =
|
|
reinterpret_cast<ExistentialMetatypeContainer *>(destLocation);
|
|
destBox = reinterpret_cast<OpaqueValue *>(&destExistential->Value);
|
|
destWitnesses = destExistential->getWitnessTables();
|
|
break;
|
|
}
|
|
case ExtendedExistentialTypeShape::SpecialKind::ExplicitLayout:
|
|
swift_unreachable("Witnesses for explicit layout not yet implemented");
|
|
}
|
|
|
|
// Fill in the trailing set of witness tables.
|
|
const unsigned numWitnessTables = witnessTables.size();
|
|
assert(numWitnessTables ==
|
|
llvm::count_if(destExistentialShape->getRequirementSignature().getRequirements(),
|
|
[](const auto &req) -> bool {
|
|
return req.getKind() ==
|
|
GenericRequirementKind::Protocol;
|
|
}));
|
|
for (unsigned i = 0; i < numWitnessTables; ++i) {
|
|
destWitnesses[i] = reinterpret_cast<const WitnessTable *>(witnessTables[i]);
|
|
}
|
|
|
|
if (takeOnSuccess) {
|
|
srcType->vw_initializeWithTake(destBox, srcValue);
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
srcType->vw_initializeWithCopy(destBox, srcValue);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** Opaque Destination ******************************/
|
|
/******************************************************************************/
|
|
|
|
static DynamicCastResult
|
|
tryCastToOpaque(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Opaque);
|
|
|
|
// There's nothing special we can do here, but we have to have this
|
|
// empty function in order for the general casting logic to run
|
|
// for these types.
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**************************** Metatype Destination ****************************/
|
|
/******************************************************************************/
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
/// Check whether an unknown class instance is actually a type/metatype object.
|
|
static const Metadata *_getUnknownClassAsMetatype(void *object) {
|
|
// Objective-C class metadata are objects, so an AnyObject (or
|
|
// NSObject) may refer to a class object.
|
|
|
|
// Test whether the object's isa is a metaclass, which indicates that
|
|
// the object is a class.
|
|
|
|
Class isa = object_getClass((id)object);
|
|
if (class_isMetaClass(isa)) {
|
|
return swift_getObjCClassMetadata((const ClassMetadata *)object);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
static DynamicCastResult
|
|
tryCastToMetatype(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::Metatype);
|
|
|
|
const MetatypeMetadata *destMetatypeType = cast<MetatypeMetadata>(destType);
|
|
MetadataKind srcKind = srcType->getKind();
|
|
switch (srcKind) {
|
|
case MetadataKind::Metatype: {
|
|
const Metadata *srcMetatype = *(const Metadata * const *) srcValue;
|
|
if (auto result = swift_dynamicCastMetatype(
|
|
srcMetatype, destMetatypeType->InstanceType)) {
|
|
*((const Metadata **) destLocation) = result;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Some classes are actually metatypes
|
|
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If object is null, then we're in compatibility mode.
|
|
// Continuing here at all is dangerous, but that's what the
|
|
// pre-Swift-5.4 casting logic did.
|
|
if (srcObject == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
if (auto metatype = _getUnknownClassAsMetatype(srcObject)) {
|
|
auto srcInnerValue = reinterpret_cast<OpaqueValue *>(&metatype);
|
|
auto srcInnerType = swift_getMetatypeMetadata(metatype);
|
|
return tryCast(destLocation, destType, srcInnerValue, srcInnerType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
}
|
|
#endif
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
/// Perform a dynamic cast of a metatype to an existential metatype type.
|
|
static DynamicCastResult
|
|
_dynamicCastMetatypeToExistentialMetatype(
|
|
OpaqueValue *destLocation, const ExistentialMetatypeMetadata *destType,
|
|
const Metadata *srcMetatype,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
// The instance type of an existential metatype must be either an
|
|
// existential or an existential metatype.
|
|
auto destMetatype
|
|
= reinterpret_cast<ExistentialMetatypeContainer *>(destLocation);
|
|
|
|
// If it's an existential, we need to check for conformances.
|
|
auto targetInstanceType = destType->InstanceType;
|
|
if (auto targetInstanceTypeAsExistential =
|
|
dyn_cast<ExistentialTypeMetadata>(targetInstanceType)) {
|
|
// Check for conformance to all the protocols.
|
|
// TODO: collect the witness tables.
|
|
const WitnessTable **conformance
|
|
= destMetatype ? destMetatype->getWitnessTables() : nullptr;
|
|
if (!_conformsToProtocols(nullptr, srcMetatype,
|
|
targetInstanceTypeAsExistential,
|
|
conformance, prohibitIsolatedConformances)) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
if (destMetatype)
|
|
destMetatype->Value = srcMetatype;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
|
|
// Otherwise, we're casting to SomeProtocol.Type.Type.
|
|
auto targetInstanceTypeAsMetatype =
|
|
cast<ExistentialMetatypeMetadata>(targetInstanceType);
|
|
|
|
// If the source type isn't a metatype, the cast fails.
|
|
auto srcMetatypeMetatype = dyn_cast<MetatypeMetadata>(srcMetatype);
|
|
if (!srcMetatypeMetatype) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
// The representation of an existential metatype remains consistent
|
|
// arbitrarily deep: a metatype, followed by some protocols. The
|
|
// protocols are the same at every level, so we can just set the
|
|
// metatype correctly and then recurse, letting the recursive call
|
|
// fill in the conformance information correctly.
|
|
|
|
// Proactively set the destination metatype so that we can tail-recur,
|
|
// unless we've already done so. There's no harm in doing this even if
|
|
// the cast fails.
|
|
if (destLocation)
|
|
*((const Metadata **) destLocation) = srcMetatype;
|
|
|
|
// Recurse.
|
|
auto srcInstanceType = srcMetatypeMetatype->InstanceType;
|
|
return _dynamicCastMetatypeToExistentialMetatype(
|
|
nullptr,
|
|
targetInstanceTypeAsMetatype,
|
|
srcInstanceType,
|
|
destFailureType,
|
|
srcFailureType,
|
|
takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
|
|
// "ExistentialMetatype" is the metatype for an existential type.
|
|
static DynamicCastResult
|
|
tryCastToExistentialMetatype(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
assert(srcType != destType);
|
|
assert(destType->getKind() == MetadataKind::ExistentialMetatype);
|
|
|
|
auto destExistentialMetatypeType
|
|
= cast<ExistentialMetatypeMetadata>(destType);
|
|
MetadataKind srcKind = srcType->getKind();
|
|
switch (srcKind) {
|
|
case MetadataKind::Metatype: { // Metatype => ExistentialMetatype
|
|
const Metadata *srcMetatype = *(const Metadata * const *) srcValue;
|
|
return _dynamicCastMetatypeToExistentialMetatype(
|
|
destLocation,
|
|
destExistentialMetatypeType,
|
|
srcMetatype,
|
|
destFailureType,
|
|
srcFailureType,
|
|
takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
// Some Obj-C classes are actually metatypes
|
|
#if SWIFT_OBJC_INTEROP
|
|
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If srcObject is null, we're in compatibility mode.
|
|
// Continuing here at al is dangerous, but that's what the
|
|
// pre-Swift-5.4 casting logic did.
|
|
if (srcObject == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
if (auto metatype = _getUnknownClassAsMetatype(srcObject)) {
|
|
return _dynamicCastMetatypeToExistentialMetatype(
|
|
destLocation,
|
|
destExistentialMetatypeType,
|
|
metatype,
|
|
destFailureType,
|
|
srcFailureType,
|
|
takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances);
|
|
}
|
|
#endif
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
default:
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/********************************** Dispatch **********************************/
|
|
/******************************************************************************/
|
|
|
|
// A layer of functions that evaluate the source and/or destination types
|
|
// in order to invoke a tailored casting operation above.
|
|
//
|
|
// This layer also deals with general issues of unwrapping box types
|
|
// and invoking bridging conversions defined via the _ObjectiveCBridgeable
|
|
// protocol.
|
|
//
|
|
// Most of the caster functions above should be fairly simple:
|
|
// * They should deal with a single target type,
|
|
// * They should assume the source is fully unwrapped,
|
|
// * They should not try to report or cleanup failure,
|
|
// * If they can take, they should report the source was destroyed.
|
|
|
|
// Based on the destination type alone, select a targeted casting function.
|
|
// This design avoids some redundant inspection of the destination type
|
|
// data, for example, when we unwrap source boxes.
|
|
static tryCastFunctionType *selectCasterForDest(const Metadata *destType) {
|
|
auto destKind = destType->getKind();
|
|
switch (destKind) {
|
|
case MetadataKind::Class:
|
|
return tryCastToSwiftClass;
|
|
case MetadataKind::Struct: {
|
|
const auto targetDescriptor = cast<StructMetadata>(destType)->Description;
|
|
if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(SS)) {
|
|
return tryCastToString;
|
|
}
|
|
if (targetDescriptor == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)) {
|
|
return tryCastToAnyHashable;
|
|
}
|
|
if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(Sa)) {
|
|
return tryCastToArray;
|
|
}
|
|
if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(SD)) {
|
|
return tryCastToDictionary;
|
|
}
|
|
if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(Sh)) {
|
|
return tryCastToSet;
|
|
}
|
|
return tryCastToStruct;
|
|
}
|
|
case MetadataKind::Enum:
|
|
return tryCastToEnum;
|
|
case MetadataKind::Optional:
|
|
return tryCastToOptional;
|
|
case MetadataKind::ForeignClass:
|
|
return tryCastToForeignClass;
|
|
case MetadataKind::ForeignReferenceType:
|
|
return tryCastToForeignReferenceType;
|
|
case MetadataKind::Opaque:
|
|
return tryCastToOpaque;
|
|
case MetadataKind::Tuple:
|
|
return tryCastToTuple;
|
|
case MetadataKind::Function:
|
|
return tryCastToFunction;
|
|
case MetadataKind::Existential: {
|
|
auto existentialType = cast<ExistentialTypeMetadata>(destType);
|
|
switch (existentialType->getRepresentation()) {
|
|
case ExistentialTypeRepresentation::Opaque:
|
|
if (existentialType->NumProtocols == 0) {
|
|
return tryCastToUnconstrainedOpaqueExistential; // => Unconstrained Any
|
|
} else {
|
|
return tryCastToConstrainedOpaqueExistential; // => Non-class-constrained protocol
|
|
}
|
|
case ExistentialTypeRepresentation::Class:
|
|
return tryCastToClassExistential; // => AnyObject, with or without protocol constraints
|
|
case ExistentialTypeRepresentation::Error: // => Error existential
|
|
return tryCastToErrorExistential;
|
|
}
|
|
swift_unreachable(
|
|
"Unknown existential type representation in dynamic cast dispatch");
|
|
}
|
|
case MetadataKind::ExtendedExistential:
|
|
return tryCastToExtendedExistential;
|
|
case MetadataKind::Metatype:
|
|
return tryCastToMetatype;
|
|
case MetadataKind::ObjCClassWrapper:
|
|
return tryCastToObjectiveCClass;
|
|
case MetadataKind::ExistentialMetatype:
|
|
return tryCastToExistentialMetatype;
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
// These are internal details of runtime-only structures,
|
|
// so will never appear in compiler-generated types.
|
|
// As such, they don't need support here.
|
|
swift_unreachable(
|
|
"Unexpected MetadataKind in dynamic cast dispatch");
|
|
return nullptr;
|
|
default:
|
|
// If you see this message, then there is a new MetadataKind that I didn't
|
|
// know about when I wrote this code. Please figure out what it is, how to
|
|
// handle it, and add a case for it.
|
|
swift_unreachable(
|
|
"Unknown MetadataKind in dynamic cast dispatch");
|
|
}
|
|
}
|
|
|
|
// This top-level driver provides the general flow for all casting
|
|
// operations. It recursively unwraps source and destination as it
|
|
// searches for a suitable conversion.
|
|
static DynamicCastResult
|
|
tryCast(
|
|
OpaqueValue *destLocation, const Metadata *destType,
|
|
OpaqueValue *srcValue, const Metadata *srcType,
|
|
const Metadata *&destFailureType, const Metadata *&srcFailureType,
|
|
bool takeOnSuccess, bool mayDeferChecks, bool prohibitIsolatedConformances)
|
|
{
|
|
destFailureType = destType;
|
|
srcFailureType = srcType;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 1. If types match exactly, we can just move/copy the data.
|
|
// (The tryCastToXyz functions never see this trivial case.)
|
|
//
|
|
if (srcType == destType) {
|
|
if (takeOnSuccess) {
|
|
destType->vw_initializeWithTake(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaTake;
|
|
} else {
|
|
destType->vw_initializeWithCopy(destLocation, srcValue);
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
|
|
auto destKind = destType->getKind();
|
|
auto srcKind = srcType->getKind();
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 2. Try directly casting the current srcValue to the target type.
|
|
// (If the dynamic type is different, try that too.)
|
|
//
|
|
auto tryCastToDestType = selectCasterForDest(destType);
|
|
if (tryCastToDestType == nullptr) {
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
auto castResult = tryCastToDestType(destLocation, destType, srcValue,
|
|
srcType, destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(castResult)) {
|
|
return castResult;
|
|
}
|
|
if (srcKind == MetadataKind::Class
|
|
|| srcKind == MetadataKind::ObjCClassWrapper
|
|
|| srcKind == MetadataKind::ForeignClass) {
|
|
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
|
|
// If srcObject is null, we're in compatibility mode.
|
|
// But we can't lookup dynamic type for a null class reference, so
|
|
// just skip this in that case.
|
|
if (srcObject != nullptr) {
|
|
auto srcDynamicType = swift_getObjectType(srcObject);
|
|
if (srcDynamicType != srcType) {
|
|
srcFailureType = srcDynamicType;
|
|
auto castResult = tryCastToDestType(
|
|
destLocation, destType, srcValue, srcDynamicType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(castResult)) {
|
|
return castResult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 3. Try recursively unwrapping _source_ boxes, including
|
|
// existentials, AnyHashable, SwiftValue, and Error.
|
|
//
|
|
switch (srcKind) {
|
|
|
|
case MetadataKind::Class: {
|
|
#if !SWIFT_OBJC_INTEROP
|
|
// Try unwrapping native __SwiftValue implementation
|
|
auto subcastResult = tryCastUnwrappingSwiftValueSource(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Try unwrapping Obj-C __SwiftValue implementation
|
|
auto subcastResult = tryCastUnwrappingObjCSwiftValueSource(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
#endif
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
// Try unwrapping Obj-C NSError container
|
|
auto innerFlags = DynamicCastFlags::Default;
|
|
if (tryDynamicCastNSErrorToValue(
|
|
destLocation, srcValue, srcType, destType, innerFlags)) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Struct: {
|
|
auto srcStructType = cast<StructMetadata>(srcType);
|
|
auto srcStructDescription = srcStructType->getDescription();
|
|
|
|
// Try unwrapping AnyHashable container
|
|
if (srcStructDescription == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)) {
|
|
if (_swift_anyHashableDownCastConditionalIndirect(
|
|
srcValue, destLocation, destType)) {
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
auto subcastResult = tryCastUnwrappingExistentialSource(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::ExistentialMetatype: {
|
|
auto subcastResult = tryCastUnwrappingExistentialMetatypeSource(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::ExtendedExistential: {
|
|
auto subcastResult = tryCastUnwrappingExtendedExistentialSource(
|
|
destLocation, destType, srcValue, srcType, destFailureType,
|
|
srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 4. Try recursively unwrapping Optionals. First try jointly unwrapping
|
|
// both source and destination, then just destination, then just source.
|
|
// Note that if both are optional, we try all three of these!
|
|
// For example, consider casting an Optional<T> to
|
|
// Optional<CustomDebugStringConvertible>. If T conforms, we need to
|
|
// unwrap both. But if it doesn't, we unwrap just the destination
|
|
// in order to cast Optional<T> to the protocol directly.
|
|
//
|
|
if (destKind == MetadataKind::Optional) {
|
|
if (srcKind == MetadataKind::Optional) {
|
|
auto subcastResult = tryCastUnwrappingOptionalBoth(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
}
|
|
auto subcastResult = tryCastUnwrappingOptionalDestination(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
}
|
|
|
|
if (srcKind == MetadataKind::Optional) {
|
|
auto subcastResult = tryCastUnwrappingOptionalSource(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks,
|
|
prohibitIsolatedConformances);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 5. Finally, explore bridging conversions via ObjectiveCBridgeable,
|
|
// Error, and __SwiftValue boxing.
|
|
//
|
|
switch (destKind) {
|
|
|
|
case MetadataKind::Optional: {
|
|
// Optional supports _ObjectiveCBridgeable from an unconstrained AnyObject
|
|
if (srcType->getKind() == MetadataKind::Existential) {
|
|
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
|
|
if ((srcExistentialType->getRepresentation() == ExistentialTypeRepresentation::Class)
|
|
&& (srcExistentialType->NumProtocols == 0)
|
|
&& (srcExistentialType->getSuperclassConstraint() == nullptr)
|
|
&& (srcExistentialType->isClassBounded())) {
|
|
auto toObjCResult = tryCastFromClassToObjCBridgeable(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, false);
|
|
if (isSuccess(toObjCResult)) {
|
|
return toObjCResult;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Existential: {
|
|
// Try general machinery for stuffing values into AnyObject:
|
|
auto destExistentialType = cast<ExistentialTypeMetadata>(destType);
|
|
if (destExistentialType->getRepresentation() == ExistentialTypeRepresentation::Class) {
|
|
// Some types have custom Objective-C bridging support...
|
|
auto subcastResult = tryCastFromObjCBridgeableToClass(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
|
|
// Other types can be boxed into a __SwiftValue container...
|
|
auto swiftValueCastResult = tryCastToClassExistentialViaSwiftValue(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
|
|
if (isSuccess(swiftValueCastResult)) {
|
|
return swiftValueCastResult;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Class:
|
|
case MetadataKind::ObjCClassWrapper:
|
|
case MetadataKind::ForeignClass: {
|
|
// Try _ObjectiveCBridgeable to bridge _to_ a class type _from_ a
|
|
// struct/enum type. Note: Despite the name, this is used for both
|
|
// Swift-Swift and Swift-ObjC bridging
|
|
if (srcKind == MetadataKind::Struct || srcKind == MetadataKind::Enum) {
|
|
auto subcastResult = tryCastFromObjCBridgeableToClass(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (destKind == MetadataKind::ObjCClassWrapper) {
|
|
// If the destination type is an NSError or NSObject, and the source type
|
|
// is an Error, then the cast might succeed by NSError bridging.
|
|
if (auto srcErrorWitness = findErrorWitness(srcType)) {
|
|
if (destType == getNSErrorMetadata()
|
|
|| destType == getNSObjectMetadata()) {
|
|
auto flags = DynamicCastFlags::Default;
|
|
auto error = dynamicCastValueToNSError(srcValue, srcType,
|
|
srcErrorWitness, flags);
|
|
*reinterpret_cast<id *>(destLocation) = error;
|
|
return DynamicCastResult::SuccessViaCopy;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum: {
|
|
// Use _ObjectiveCBridgeable to bridge _from_ a class type _to_ a
|
|
// struct/enum type. Note: Despite the name, this is used for both
|
|
// Swift-Swift and ObjC-Swift bridging
|
|
if (srcKind == MetadataKind::Class
|
|
|| srcKind == MetadataKind::ObjCClassWrapper
|
|
|| srcKind == MetadataKind::ForeignClass) {
|
|
auto subcastResult = tryCastFromClassToObjCBridgeable(
|
|
destLocation, destType, srcValue, srcType,
|
|
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
|
|
if (isSuccess(subcastResult)) {
|
|
return subcastResult;
|
|
}
|
|
}
|
|
|
|
// Note: In theory, if src and dest are both struct/enum types, we could see
|
|
// if the ObjC bridgeable class types matched and then do a two-step
|
|
// conversion from src -> bridge class -> dest. Such ambitious conversions
|
|
// might cause more harm than good, though. In particular, it could
|
|
// undermine code that uses a series of `as?` to quickly determine how to
|
|
// handle a particular object.
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DynamicCastResult::Failure;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/****************************** Main Entrypoint *******************************/
|
|
/******************************************************************************/
|
|
|
|
/// ABI: Perform a dynamic cast to an arbitrary type.
|
|
static bool
|
|
swift_dynamicCastImpl(OpaqueValue *destLocation,
|
|
OpaqueValue *srcValue,
|
|
const Metadata *srcType,
|
|
const Metadata *destType,
|
|
DynamicCastFlags flags)
|
|
{
|
|
// If the compiler has asked for a "take", we can
|
|
// move pointers without ref-counting overhead.
|
|
bool takeOnSuccess = flags & DynamicCastFlags::TakeOnSuccess;
|
|
// Unconditional casts are allowed to crash the program on failure.
|
|
// We can exploit that for performance: return a partial conversion
|
|
// immediately and do additional checks lazily when the results are
|
|
// actually accessed.
|
|
bool mayDeferChecks = flags & DynamicCastFlags::Unconditional;
|
|
|
|
// Whether the compiler told us that we aren't allowed to use *any* isolated
|
|
// conformances, regardless of whether we are in that isolation domain.
|
|
bool prohibitIsolatedConformances =
|
|
flags & DynamicCastFlags::ProhibitIsolatedConformances;
|
|
|
|
// Attempt the cast...
|
|
const Metadata *destFailureType = destType;
|
|
const Metadata *srcFailureType = srcType;
|
|
auto result = tryCast(
|
|
destLocation, destType,
|
|
srcValue, srcType,
|
|
destFailureType, srcFailureType,
|
|
takeOnSuccess, mayDeferChecks, prohibitIsolatedConformances);
|
|
|
|
switch (result) {
|
|
case DynamicCastResult::Failure:
|
|
if (flags & DynamicCastFlags::Unconditional) {
|
|
swift_dynamicCastFailure(srcFailureType, destFailureType);
|
|
}
|
|
if (flags & DynamicCastFlags::DestroyOnFailure) {
|
|
srcType->vw_destroy(srcValue);
|
|
}
|
|
return false;
|
|
case DynamicCastResult::SuccessViaCopy:
|
|
if (takeOnSuccess) { // We copied, but compiler asked for take.
|
|
srcType->vw_destroy(srcValue);
|
|
}
|
|
return true;
|
|
case DynamicCastResult::SuccessViaTake:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#define OVERRIDE_DYNAMICCASTING COMPATIBILITY_OVERRIDE
|
|
#include "../CompatibilityOverride/CompatibilityOverrideIncludePath.h"
|