Add swift_dynamicCast.

This is the most general dynamic cast operation, permitting
arbitary source and destination types and handling arbitrary
changes in representation.  A value of the destination type
is constructed in an address provided by the caller; flags
control the behavior w.r.t. the source value.

Not yet used; probably buggy in various particulars.

Swift SVN r18815
This commit is contained in:
John McCall
2014-06-12 00:13:13 +00:00
parent a581511069
commit 9890d8bca0
6 changed files with 735 additions and 67 deletions

View File

@@ -14,6 +14,7 @@
//
//===----------------------------------------------------------------------===//
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Fallthrough.h"
#include "swift/Runtime/Enum.h"
#include "swift/Runtime/HeapObject.h"
@@ -21,6 +22,7 @@
#include "llvm/ADT/DenseMap.h"
#include "Debug.h"
#include "ExistentialMetadataImpl.h"
#include "Private.h"
#include <dlfcn.h>
@@ -35,6 +37,7 @@ using namespace metadataimpl;
extern "C" const ClassMetadata* object_getClass(const void *);
extern "C" const char* class_getName(const ClassMetadata*);
extern "C" const ClassMetadata* class_getSuperclass(const ClassMetadata*);
extern "C" const Metadata *swift_getObjectType(const void *);
// Aliases for Swift runtime entry points for Objective-C types.
extern "C" const void *swift_dynamicCastObjCProtocolConditional(
@@ -42,6 +45,31 @@ extern "C" const void *swift_dynamicCastObjCProtocolConditional(
size_t numProtocols,
const ProtocolDescriptor * const *protocols);
/// Report a dynamic cast failure.
LLVM_ATTRIBUTE_NORETURN
LLVM_ATTRIBUTE_ALWAYS_INLINE // Minimize trashed registers
static void _dynamicCastFailure(const Metadata *sourceType,
const Metadata *targetType) {
swift::crash("Swift dynamic cast failure");
}
/// Report a corrupted type object.
LLVM_ATTRIBUTE_NORETURN
LLVM_ATTRIBUTE_ALWAYS_INLINE // Minimize trashed registers
static void _failCorruptType(const Metadata *type) {
swift::crash("Corrupt Swift type object");
}
/// A convenient method for failing out of a dynamic cast.
static bool _fail(OpaqueValue *srcValue, const Metadata *srcType,
const Metadata *targetType, DynamicCastFlags flags) {
if (flags & DynamicCastFlags::Unconditional)
_dynamicCastFailure(srcType, targetType);
if (flags & DynamicCastFlags::DestroyOnFailure)
srcType->vw_destroy(srcValue);
return false;
}
static size_t
_setupClassMask() {
void *handle = dlopen(nullptr, RTLD_LAZY);
@@ -101,72 +129,40 @@ swift::swift_dynamicCastClassUnconditional(const void *object,
return value;
}
static const OpaqueValue *
_dynamicCastToExistential(const OpaqueValue *value,
const Metadata *sourceType,
const ExistentialTypeMetadata *targetType) {
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
auto *protocol = targetType->Protocols[i];
// Handle AnyObject directly.
// FIXME: strcmp here is horribly slow.
if (strcmp(protocol->Name, "_TtPSs9AnyObject_") == 0) {
switch (sourceType->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass: // FIXME
// Classes conform to AnyObject.
break;
case MetadataKind::Existential: {
auto sourceExistential
= static_cast<const ExistentialTypeMetadata*>(sourceType);
// The existential conforms to AnyObject if it's class-constrained.
if (sourceExistential->Flags.getClassConstraint()
!= ProtocolClassConstraint::Class)
return nullptr;
break;
}
case MetadataKind::ExistentialMetatype: // FIXME
case MetadataKind::Function:
case MetadataKind::Block: // FIXME
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Metatype:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
return nullptr;
}
continue;
}
// FIXME: Can't handle protocols that require witness tables.
if (protocol->Flags.needsWitnessTable())
return nullptr;
// For Objective-C protocols, check whether we have a class that
// conforms to the given protocol.
switch (sourceType->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper: {
const void *object
= *reinterpret_cast<const void * const *>(value);
if (swift_dynamicCastObjCProtocolConditional(object, 1, &protocol)) {
continue;
}
return nullptr;
}
static bool _unknownClassConformsToObjCProtocol(const OpaqueValue *value,
const ProtocolDescriptor *protocol) {
const void *object
= *reinterpret_cast<const void * const *>(value);
return swift_dynamicCastObjCProtocolConditional(object, 1, &protocol);
}
/// Check whether a type conforms to a protocol.
///
/// \param value - can be null, in which case the question should
/// be answered abstractly if possible
/// \param conformance - if non-null, and the protocol requires a
/// witness table, and the type implements the protocol, the witness
/// table will be placed here
static bool _conformsToProtocol(const OpaqueValue *value,
const Metadata *type,
const ProtocolDescriptor *protocol,
const void **conformance) {
// Handle AnyObject directly.
// FIXME: strcmp here is horribly slow.
if (strcmp(protocol->Name, "_TtPSs9AnyObject_") == 0) {
switch (type->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
return nullptr;
// Classes conform to AnyObject.
return true;
case MetadataKind::Existential: // FIXME
case MetadataKind::Existential: {
auto sourceExistential = cast<ExistentialTypeMetadata>(type);
// The existential conforms to AnyObject if it's class-constrained.
return sourceExistential->isClassBounded();
}
case MetadataKind::ExistentialMetatype: // FIXME
case MetadataKind::Function:
case MetadataKind::Block: // FIXME
@@ -178,15 +174,204 @@ _dynamicCastToExistential(const OpaqueValue *value,
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
return nullptr;
return false;
}
_failCorruptType(type);
}
// FIXME: Can't handle protocols that require witness tables.
if (protocol->Flags.needsWitnessTable())
return false;
// For Objective-C protocols, check whether we have a class that
// conforms to the given protocol.
switch (type->getKind()) {
case MetadataKind::Class:
if (value) {
return _unknownClassConformsToObjCProtocol(value, protocol);
} else {
return _swift_classConformsToObjCProtocol(type, protocol);
}
return nullptr;
case MetadataKind::ObjCClassWrapper: {
if (value) {
return _unknownClassConformsToObjCProtocol(value, protocol);
} else {
auto wrapper = cast<ObjCClassWrapperMetadata>(type);
return _swift_classConformsToObjCProtocol(wrapper->Class, protocol);
}
}
case MetadataKind::ForeignClass:
if (value)
return _unknownClassConformsToObjCProtocol(value, protocol);
return false;
case MetadataKind::Existential: // FIXME
case MetadataKind::ExistentialMetatype: // FIXME
case MetadataKind::Function:
case MetadataKind::Block: // FIXME
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Metatype:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
return false;
}
return false;
}
/// 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 ProtocolDescriptorList &protocols,
const void **conformances) {
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
const ProtocolDescriptor *protocol = protocols[i];
if (!_conformsToProtocol(value, type, protocol, conformances))
return false;
if (protocol->Flags.needsWitnessTable()) {
assert(*conformances != nullptr);
++conformances;
}
}
return true;
}
static const OpaqueValue *
_dynamicCastToExistential(const OpaqueValue *value,
const Metadata *sourceType,
const ExistentialTypeMetadata *targetType) {
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
auto *protocol = targetType->Protocols[i];
if (!_conformsToProtocol(value, sourceType, protocol, nullptr))
return nullptr;
}
return value;
}
/// Given a possibly-existential value, find its dynamic type and the
/// address of its storage.
static void findDynamicValueAndType(OpaqueValue *value, const Metadata *type,
OpaqueValue *&outValue,
const Metadata *&outType) {
switch (type->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass: {
// TODO: avoid unnecessary repeat lookup of
// ObjCClassWrapper/ForeignClass when the type matches.
outValue = value;
outType = swift_getObjectType(*reinterpret_cast<void**>(value));
return;
}
case MetadataKind::Existential: {
auto existentialType = cast<ExistentialTypeMetadata>(type);
if (existentialType->isClassBounded()) {
auto existential =
reinterpret_cast<ClassExistentialContainer*>(value);
outValue = (OpaqueValue*) &existential->Value;
outType = swift_getObjectType(existential->Value);
return;
} else {
auto existential =
reinterpret_cast<OpaqueExistentialContainer*>(value);
OpaqueValue *existentialValue =
existential->Type->vw_projectBuffer(&existential->Buffer);
findDynamicValueAndType(existentialValue, existential->Type,
outValue, outType);
return;
}
}
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype: {
auto storedType = *(const Metadata **) value;
outValue = value;
outType = swift_getMetatypeMetadata(storedType);
return;
}
// Non-polymorphic types.
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
outValue = value;
outType = type;
return;
}
_failCorruptType(type);
}
/// Perform a dynamic cast to an existential type.
static bool _dynamicCastToExistential(OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const ExistentialTypeMetadata *targetType,
DynamicCastFlags flags) {
// Find the actual type of the source.
OpaqueValue *srcDynamicValue;
const Metadata *srcDynamicType;
findDynamicValueAndType(src, srcType, srcDynamicValue, srcDynamicType);
// The representation of an existential is different for
// class-bounded protocols.
if (targetType->isClassBounded()) {
auto destExistential =
reinterpret_cast<ClassExistentialContainer*>(dest);
// Check for protocol conformances and fill in the witness tables.
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
targetType->Protocols,
destExistential->getWitnessTables()))
return _fail(srcDynamicValue, srcDynamicType, targetType, flags);
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
destExistential->Value = object;
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
swift_retain_noresult(object);
}
return true;
} else {
auto destExistential =
reinterpret_cast<OpaqueExistentialContainer*>(dest);
// Check for protocol conformances and fill in the witness tables.
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
targetType->Protocols,
destExistential->getWitnessTables()))
return _fail(srcDynamicValue, srcDynamicType, targetType, flags);
// Fill in the type and value.
destExistential->Type = srcDynamicType;
if (flags & DynamicCastFlags::TakeOnSuccess) {
srcDynamicType->vw_initializeBufferWithTake(&destExistential->Buffer,
srcDynamicValue);
} else {
srcDynamicType->vw_initializeBufferWithCopy(&destExistential->Buffer,
srcDynamicValue);
}
return true;
}
}
/// Perform a dynamic class of some sort of class instance to some
/// sort of class type.
const void *
swift::swift_dynamicCastUnknownClass(const void *object,
const Metadata *targetType) {
@@ -221,6 +406,8 @@ swift::swift_dynamicCastUnknownClass(const void *object,
swift::crash("bad metadata kind!");
}
/// Perform a dynamic class of some sort of class instance to some
/// sort of class type.
const void *
swift::swift_dynamicCastUnknownClassUnconditional(const void *object,
const Metadata *targetType) {
@@ -363,7 +550,7 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
swift::crash("Swift dynamic cast failed");
_dynamicCastFailure(sourceType, targetType);
}
break;
@@ -383,11 +570,435 @@ swift::swift_dynamicCastMetatypeUnconditional(const Metadata *sourceType,
// The cast succeeds only if the metadata pointers are statically
// equivalent.
if (sourceType != targetType)
swift::crash("Swift dynamic cast failed");
_dynamicCastFailure(sourceType, targetType);
return origSourceType;
}
}
/// Do a dynamic cast to the target class.
static bool _dynamicCastUnknownClass(OpaqueValue *dest,
void *object,
const Metadata *targetType,
DynamicCastFlags flags) {
void **destSlot = reinterpret_cast<void **>(dest);
// The unconditional path avoids some failure logic.
if (flags & DynamicCastFlags::Unconditional) {
void *result = const_cast<void*>(
swift_dynamicCastUnknownClassUnconditional(object, targetType));
*destSlot = result;
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
#if SWIFT_OBJC_INTEROP
swift_unknownRetain(result);
#else
swift_retain(result);
#endif
}
return true;
}
// Okay, we're doing a conditional cast.
void *result =
const_cast<void*>(swift_dynamicCastUnknownClass(object, targetType));
assert(result == nullptr || object == result);
// If the cast failed, destroy the input and return false.
if (!result) {
if (flags & DynamicCastFlags::DestroyOnFailure) {
#if SWIFT_OBJC_INTEROP
swift_unknownRelease(object);
#else
swift_release(object);
#endif
}
return false;
}
// Otherwise, store to the destination and return true.
*destSlot = result;
if (!(flags & DynamicCastFlags::TakeOnSuccess)) {
#if SWIFT_OBJC_INTEROP
swift_unknownRetain(result);
#else
swift_retain(result);
#endif
}
return true;
}
/// Perform a dynamic cast from an existential type to some kind of
/// class type.
static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest,
OpaqueValue *src,
const ExistentialTypeMetadata *srcType,
const Metadata *targetType,
DynamicCastFlags flags) {
if (srcType->isClassBounded()) {
auto classContainer =
reinterpret_cast<ClassExistentialContainer*>(src);
void *obj = classContainer->Value;
return _dynamicCastUnknownClass(dest, obj, targetType, flags);
} else {
auto opaqueContainer =
reinterpret_cast<OpaqueExistentialContainer*>(src);
auto srcCapturedType = opaqueContainer->Type;
return swift_dynamicCast(dest,
srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer),
srcCapturedType,
targetType,
flags);
}
}
/// Perform a dynamic cast from an existential type to a
/// non-existential type.
static bool _dynamicCastFromExistential(OpaqueValue *dest,
OpaqueValue *src,
const ExistentialTypeMetadata *srcType,
const Metadata *targetType,
DynamicCastFlags flags) {
OpaqueValue *srcValue;
const Metadata *srcCapturedType;
if (srcType->isClassBounded()) {
auto classContainer =
reinterpret_cast<const ClassExistentialContainer*>(src);
srcValue = (OpaqueValue*) &classContainer->Value;
void *obj = classContainer->Value;
srcCapturedType = swift_unknownTypeOf(reinterpret_cast<HeapObject*>(obj));
} else {
auto opaqueContainer = reinterpret_cast<OpaqueExistentialContainer*>(src);
srcCapturedType = opaqueContainer->Type;
srcValue = srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
}
return swift_dynamicCast(dest, srcValue, srcCapturedType,
targetType, flags);
}
/// Perform a dynamic cast of a metatype to a metatype.
static bool _dynamicCastMetatypeToMetatype(OpaqueValue *dest,
const Metadata *metatype,
const Metadata *targetType,
DynamicCastFlags flags) {
const Metadata *result;
if (flags & DynamicCastFlags::Unconditional) {
result = swift_dynamicCastMetatypeUnconditional(metatype, targetType);
} else {
result = swift_dynamicCastMetatype(metatype, targetType);
if (!result) return false;
}
*((const Metadata **) dest) = result;
return true;
}
/// Check whether an unknown class instance is actually a class object.
static const Metadata *_getUnknownClassAsMetatype(void *object) {
// Class values are currently never metatypes (?).
return nullptr;
}
/// Perform a dynamic cast of a class value to a metatype type.
static bool _dynamicCastUnknownClassToMetatype(OpaqueValue *dest,
void *object,
const MetatypeMetadata *targetType,
DynamicCastFlags flags) {
if (auto metatype = _getUnknownClassAsMetatype(object))
return _dynamicCastMetatypeToMetatype(dest, metatype, targetType, flags);
if (flags & DynamicCastFlags::Unconditional)
_dynamicCastFailure(swift_getObjectType(object), targetType);
if (flags & DynamicCastFlags::DestroyOnFailure)
swift_release((HeapObject*) object);
return false;
}
/// Perform a dynamic cast to a metatype type.
static bool _dynamicCastToMetatype(OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const MetatypeMetadata *targetType,
DynamicCastFlags flags) {
switch (srcType->getKind()) {
case MetadataKind::Metatype: {
const Metadata *srcMetatype = *(const Metadata * const *) src;
return _dynamicCastMetatypeToMetatype(dest, srcMetatype,
targetType, flags);
}
case MetadataKind::ExistentialMetatype: {
const Metadata *srcMetatype = *(const Metadata * const *) src;
return _dynamicCastMetatypeToMetatype(dest, srcMetatype,
targetType, flags);
}
case MetadataKind::Existential: {
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
if (srcExistentialType->isClassBounded()) {
auto srcExistential = (ClassExistentialContainer*) src;
return _dynamicCastUnknownClassToMetatype(dest,
srcExistential->Value,
targetType, flags);
} else {
auto srcExistential = (OpaqueExistentialContainer*) src;
auto srcValueType = srcExistential->Type;
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
return _dynamicCastToMetatype(dest, srcValue, srcValueType,
targetType, flags);
}
}
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass: {
auto object = reinterpret_cast<void**>(src);
return _dynamicCastUnknownClassToMetatype(dest, object, targetType, flags);
}
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
return _fail(src, srcType, targetType, flags);
}
_failCorruptType(srcType);
}
/// Perform a dynamic cast of a metatype to an existential metatype type.
static bool _dynamicCastMetatypeToExistentialMetatype(OpaqueValue *dest,
const Metadata *srcMetatype,
const ExistentialMetatypeMetadata *targetType,
DynamicCastFlags flags,
bool writeDestMetatype = true) {
// The instance type of an existential metatype must be either an
// existential or an existential metatype.
// If it's an existential, we need to check for conformances.
auto targetInstanceType = targetType->InstanceType;
if (auto targetInstanceTypeAsExistential =
dyn_cast<ExistentialTypeMetadata>(targetInstanceType)) {
// Check for conformance to all the protocols.
// TODO: collect the witness tables.
auto &protocols = targetInstanceTypeAsExistential->Protocols;
for (unsigned i = 0, n = protocols.NumProtocols; i != n; ++i) {
const ProtocolDescriptor *protocol = protocols[i];
if (!_conformsToProtocol(nullptr, srcMetatype, protocol, nullptr)) {
if (flags & DynamicCastFlags::Unconditional)
_dynamicCastFailure(srcMetatype, targetType);
return false;
}
}
if (writeDestMetatype)
*((const Metadata **) dest) = srcMetatype;
return true;
}
// 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) {
if (flags & DynamicCastFlags::Unconditional)
_dynamicCastFailure(srcMetatype, targetType);
return false;
}
// 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-recurse,
// unless we've already done so. There's no harm in doing this even if
// the cast fails.
if (writeDestMetatype)
*((const Metadata **) dest) = srcMetatype;
// Recurse.
auto srcInstanceType = srcMetatypeMetatype->InstanceType;
return _dynamicCastMetatypeToExistentialMetatype(dest, srcInstanceType,
targetInstanceTypeAsMetatype,
flags,
/*overwrite*/ false);
}
/// Perform a dynamic cast of a class value to an existential metatype type.
static bool _dynamicCastUnknownClassToExistentialMetatype(OpaqueValue *dest,
void *object,
const ExistentialMetatypeMetadata *targetType,
DynamicCastFlags flags) {
if (auto metatype = _getUnknownClassAsMetatype(object))
return _dynamicCastMetatypeToExistentialMetatype(dest, metatype,
targetType, flags);
// Class values are currently never metatypes (?).
if (flags & DynamicCastFlags::Unconditional)
_dynamicCastFailure(swift_getObjectType(object), targetType);
if (flags & DynamicCastFlags::DestroyOnFailure)
swift_release((HeapObject*) object);
return false;
}
/// Perform a dynamic cast to an existential metatype type.
static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const ExistentialMetatypeMetadata *targetType,
DynamicCastFlags flags) {
switch (srcType->getKind()) {
case MetadataKind::Metatype: {
const Metadata *srcMetatype = *(const Metadata * const *) src;
return _dynamicCastMetatypeToExistentialMetatype(dest, srcMetatype,
targetType, flags);
}
// TODO: take advantage of protocol conformances already known.
case MetadataKind::ExistentialMetatype: {
const Metadata *srcMetatype = *(const Metadata * const *) src;
return _dynamicCastMetatypeToExistentialMetatype(dest, srcMetatype,
targetType, flags);
}
case MetadataKind::Existential: {
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
if (srcExistentialType->isClassBounded()) {
auto srcExistential = (ClassExistentialContainer*) src;
return _dynamicCastUnknownClassToExistentialMetatype(dest,
srcExistential->Value,
targetType, flags);
} else {
auto srcExistential = (OpaqueExistentialContainer*) src;
auto srcValueType = srcExistential->Type;
auto srcValue = srcValueType->vw_projectBuffer(&srcExistential->Buffer);
return _dynamicCastToExistentialMetatype(dest, srcValue, srcValueType,
targetType, flags);
}
}
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
if (flags & DynamicCastFlags::Unconditional) {
_dynamicCastFailure(srcType, targetType);
}
return false;
}
_failCorruptType(srcType);
}
/// Perform a dynamic cast to an arbitrary type.
bool swift::swift_dynamicCast(OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
DynamicCastFlags flags) {
switch (targetType->getKind()) {
// Casts to class type.
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
switch (srcType->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass: {
// Do a dynamic cast on the instance pointer.
void *object = *reinterpret_cast<void * const *>(src);
return _dynamicCastUnknownClass(dest, object,
targetType, flags);
}
case MetadataKind::Existential: {
auto srcExistentialType = cast<ExistentialTypeMetadata>(srcType);
return _dynamicCastToUnknownClassFromExistential(dest, src,
srcExistentialType,
targetType, flags);
}
case MetadataKind::ExistentialMetatype:
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Metatype:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
return nullptr;
}
break;
case MetadataKind::Existential:
return _dynamicCastToExistential(dest, src, srcType,
cast<ExistentialTypeMetadata>(targetType),
flags);
case MetadataKind::Metatype:
return _dynamicCastToMetatype(dest, src, srcType,
cast<MetatypeMetadata>(targetType),
flags);
case MetadataKind::ExistentialMetatype:
return _dynamicCastToExistentialMetatype(dest, src, srcType,
cast<ExistentialMetatypeMetadata>(targetType),
flags);
// The non-polymorphic types.
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Enum:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Struct:
case MetadataKind::Tuple:
// If there's an exact type match, we're done.
if (srcType == targetType) {
if (flags & DynamicCastFlags::TakeOnSuccess) {
srcType->vw_initializeWithTake(dest, src);
} else {
srcType->vw_initializeWithCopy(dest, src);
}
return true;
}
// If we have an existential, look at its dynamic type.
if (auto srcExistentialType = dyn_cast<ExistentialTypeMetadata>(srcType)) {
return _dynamicCastFromExistential(dest, src, srcExistentialType,
targetType, flags);
}
// Otherwise, we have a failure.
return _fail(src, srcType, targetType, flags);
}
_failCorruptType(srcType);
}
const OpaqueValue *
swift::swift_dynamicCastIndirect(const OpaqueValue *value,
const Metadata *sourceType,