Files
swift-mirror/stdlib/public/runtime/DynamicCast.cpp
David Smith 08233afee9 Extend the existing memoization of NSString's Hashable conformance to cover AnyObjects that are actually NSStrings as well (#82440)
Fixes rdar://154123172 (Consider memoizing the lookup for NSString's
conformance to Hashable)
2025-06-24 08:21:13 -07:00

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"