Files
swift-mirror/stdlib/public/runtime/DynamicCast.cpp
tbkka 524cfae1b2 [Dynamic Casting] Overhauled Runtime (#33561)
* Dynamic Cast Rework: Runtime

This is a completely refactored version of the core swift_dynamicCast
runtime method.

This fixes a number of bugs, especially in the handling of multiply-wrapped
types such as Optional within Any.  The result should be much closer to the
behavior specified by `docs/DynamicCasting.md`.

Most of the type-specific logic is simply copied over from the
earlier implementation, but the overall structure has been changed
to be uniformly recursive.  In particular, this provides uniform
handling of Optional, existentials, Any and other common "box"
types along all paths.  The consistent structure should also be
easier to update in the future with new general types.

Benchmarking does not show any noticable performance implications.

**Temporarily**, the old implementation is still available.  Setting the
environment variable `SWIFT_OLD_DYNAMIC_CAST_RUNTIME` before launching a program
will use the old runtime implementation.  This is only to facilitate testing;
once the new implementation is stable, I expect to completely remove the old
implementation.
2020-08-27 11:06:40 -07:00

2198 lines
82 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.h"
#include "ErrorObject.h"
#include "Private.h"
#include "SwiftHashableSupport.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/Basic/Lazy.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
);
// 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);
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);
swift::fatalError(/* flags = */ 0,
"Found unexpected null pointer value"
" while trying to cast value of type '%s' (%p)"
" to '%s' (%p)\n",
srcTypeName.c_str(), srcType,
destTypeName.c_str(), destType);
}
/******************************************************************************/
/******************************* 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 {
static_assert(WitnessTableFirstRequirementOffset == 1,
"Witness table layout changed");
// associatedtype _ObjectiveCType : class
void *_ObjectiveCType;
// func _bridgeToObjectiveC() -> _ObjectiveCType
SWIFT_CC(swift)
HeapObject *(*bridgeToObjectiveC)(
SWIFT_CONTEXT OpaqueValue *self, const Metadata *Self,
const _ObjectiveCBridgeableWitnessTable *witnessTable);
// class func _forceBridgeFromObjectiveC(x: _ObjectiveCType,
// inout result: Self?)
SWIFT_CC(swift)
void (*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 (*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);
static const _ObjectiveCBridgeableWitnessTable *
findBridgeWitness(const Metadata *T) {
static const auto bridgeableProtocol
= &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable);
auto w = swift_conformsToProtocol(T, bridgeableProtocol);
return reinterpret_cast<const _ObjectiveCBridgeableWitnessTable *>(w);
}
/// 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(std::max_align_t) 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. Sanity 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);
if (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)
{
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);
}
#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)
{
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 *object = getNonNullSrcObject(srcValue, srcType, destType);
if (auto t = swift_dynamicCastClass(object, 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)
{
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);
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)
{
#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 (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;
}
/******************************************************************************/
/***************************** 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)
{
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 repeeated
// 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 = findBridgeWitness(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);
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);
}
};
#endif
static DynamicCastResult
tryCastToAnyHashable(
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::Struct);
assert(cast<StructMetadata>(destType)->Description
== &STRUCT_TYPE_DESCR_SYM(s11AnyHashable));
auto hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
swift_conformsToProtocol(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)
{
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)
{
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)
{
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)
{
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);
#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)
{
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)
{
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
copyNil(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)
{
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) {
copyNil(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);
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)
{
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);
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)
{
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);
}
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)
{
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;
}
}
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);
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)
{
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) {
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::_conformsToProtocol(value, type, protocol, conformances))
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)
{
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)
{
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())) {
return tryCastToUnconstrainedOpaqueExistential(
destLocation, destType, srcValue, srcType,
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
} 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)
{
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())) {
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:
case MetadataKind::Class:
case MetadataKind::ForeignClass: {
auto object = getNonNullSrcObject(srcValue, srcType, destType);
if (_conformsToProtocols(srcValue, srcType,
destExistentialType,
destExistentialLocation->getWitnessTables())) {
destExistentialLocation->Value = object;
if (takeOnSuccess) {
return DynamicCastResult::SuccessViaTake;
} else {
swift_unknownObjectRetain(object);
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);
// Fail if the target has constraints that make it unsuitable for
// a __SwiftValue box.
// FIXME: We should not have different checks here for
// Obj-C vs non-Obj-C. The _SwiftValue boxes should conform
// to the exact same protocols on both platforms.
bool destIsConstrained = destExistentialType->NumProtocols != 0;
if (destIsConstrained) {
#if SWIFT_OBJC_INTEROP // __SwiftValue is an Obj-C class
if (!findSwiftValueConformances(
destExistentialType, destExistentialLocation->getWitnessTables())) {
return DynamicCastResult::Failure;
}
#else // __SwiftValue is a native class
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)
{
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)) {
#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)
{
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);
}
/******************************************************************************/
/**************************** 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)
{
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)
{
assert(srcType != destType);
assert(destType->getKind() == MetadataKind::Metatype);
const MetatypeMetadata *destMetatypeType = cast<MetatypeMetadata>(destType);
MetadataKind srcKind = srcType->getKind();
switch (srcKind) {
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype: {
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 *object = getNonNullSrcObject(srcValue, srcType, destType);
if (auto metatype = _getUnknownClassAsMetatype(object)) {
auto srcInnerValue = reinterpret_cast<OpaqueValue *>(&metatype);
auto srcInnerType = swift_getMetatypeMetadata(metatype);
return tryCast(destLocation, destType, srcInnerValue, srcInnerType,
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
}
#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)
{
// 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)) {
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);
}
// "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)
{
assert(srcType != destType);
assert(destType->getKind() == MetadataKind::ExistentialMetatype);
auto destExistentialMetatypeType
= cast<ExistentialMetatypeMetadata>(destType);
MetadataKind srcKind = srcType->getKind();
switch (srcKind) {
case MetadataKind::Metatype: // Metatype => ExistentialMetatype
case MetadataKind::ExistentialMetatype: { // ExistentialMetatype => ExistentialMetatype
const Metadata *srcMetatype = *(const Metadata * const *) srcValue;
return _dynamicCastMetatypeToExistentialMetatype(
destLocation,
destExistentialMetatypeType,
srcMetatype,
destFailureType,
srcFailureType,
takeOnSuccess, mayDeferChecks);
}
case MetadataKind::ObjCClassWrapper: {
// Some Obj-C classes are actually metatypes
#if SWIFT_OBJC_INTEROP
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
if (auto metatype = _getUnknownClassAsMetatype(srcObject)) {
return _dynamicCastMetatypeToExistentialMetatype(
destLocation,
destExistentialMetatypeType,
metatype,
destFailureType,
srcFailureType,
takeOnSuccess, mayDeferChecks);
}
#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::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_runtime_unreachable(
"Unknown existential type representation in dynamic cast dispatch");
}
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_runtime_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_runtime_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)
{
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);
if (isSuccess(castResult)) {
return castResult;
}
if (srcKind == MetadataKind::Class
|| srcKind == MetadataKind::ObjCClassWrapper
|| srcKind == MetadataKind::ForeignClass) {
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
auto srcDynamicType = swift_getObjectType(srcObject);
if (srcDynamicType != srcType) {
srcFailureType = srcDynamicType;
auto castResult = tryCastToDestType(
destLocation, destType, srcValue, srcDynamicType,
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
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
if (swift_unboxFromSwiftValueWithType(srcValue, destLocation, destType)) {
return DynamicCastResult::SuccessViaCopy;
}
#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);
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);
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);
if (isSuccess(subcastResult)) {
return subcastResult;
}
}
auto subcastResult = tryCastUnwrappingOptionalDestination(
destLocation, destType, srcValue, srcType,
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
if (isSuccess(subcastResult)) {
return subcastResult;
}
}
if (srcKind == MetadataKind::Optional) {
auto subcastResult = tryCastUnwrappingOptionalSource(
destLocation, destType, srcValue, srcType,
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
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 *******************************/
/******************************************************************************/
// XXX REMOVE ME XXX TODO XXX
// Declare the old entrypoint
SWIFT_RUNTIME_EXPORT
bool
swift_dynamicCast_OLD(OpaqueValue *destLocation,
OpaqueValue *srcValue,
const Metadata *srcType,
const Metadata *destType,
DynamicCastFlags flags);
// XXX REMOVE ME XXX TODO XXX
/// 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)
{
// XXX REMOVE ME XXX TODO XXX TRANSITION SHIM
// XXX REMOVE ME XXX TODO XXX TRANSITION SHIM
// Support switching to the old implementation while the new one
// is still settling. Once the new implementation is stable,
// I'll rip the old one entirely out.
static bool useOldImplementation = false; // Default: NEW Implementation
static swift_once_t Predicate;
swift_once(
&Predicate,
[](void *) {
// Define SWIFT_OLD_DYNAMIC_CAST_RUNTIME=1 to use the old runtime
// dynamic cast logic.
auto useOld = getenv("SWIFT_OLD_DYNAMIC_CAST_RUNTIME");
if (useOld) {
useOldImplementation = true;
}
}, nullptr);
if (useOldImplementation) {
return swift_dynamicCast_OLD(destLocation, srcValue,
srcType, destType, flags);
}
// XXX REMOVE ME XXX TODO XXX TRANSITION SHIM
// XXX REMOVE ME XXX TODO XXX TRANSITION SHIM
// 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;
// Attempt the cast...
const Metadata *destFailureType = destType;
const Metadata *srcFailureType = srcType;
auto result = tryCast(
destLocation, destType,
srcValue, srcType,
destFailureType, srcFailureType,
takeOnSuccess, mayDeferChecks);
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.def"