Dynamic casting: handle value-to-class-existential casts via bridging.

This allows one to cast a value type (say, [String]) to AnyObject or
NSCoding, for example. It's another piece of <rdar://problem/17637959>.


Swift SVN r20964
This commit is contained in:
Doug Gregor
2014-08-03 05:15:47 +00:00
parent 4c06dff759
commit c66a1ef98f
3 changed files with 164 additions and 28 deletions

View File

@@ -114,6 +114,9 @@ inline bool operator&(DynamicCastFlags a, DynamicCastFlags b) {
inline DynamicCastFlags operator|(DynamicCastFlags a, DynamicCastFlags b) { inline DynamicCastFlags operator|(DynamicCastFlags a, DynamicCastFlags b) {
return DynamicCastFlags(size_t(a) | size_t(b)); return DynamicCastFlags(size_t(a) | size_t(b));
} }
inline DynamicCastFlags operator-(DynamicCastFlags a, DynamicCastFlags b) {
return DynamicCastFlags(size_t(a) & ~size_t(b));
}
inline DynamicCastFlags &operator|=(DynamicCastFlags &a, DynamicCastFlags b) { inline DynamicCastFlags &operator|=(DynamicCastFlags &a, DynamicCastFlags b) {
return a = (a | b); return a = (a | b);
} }

View File

@@ -66,6 +66,30 @@ namespace {
static const _ObjectiveCBridgeableWitnessTable * static const _ObjectiveCBridgeableWitnessTable *
findBridgeWitness(const Metadata *T); findBridgeWitness(const Metadata *T);
static bool _dynamicCastValueToClassViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
DynamicCastFlags flags);
static bool _dynamicCastValueToClassExistentialViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const ExistentialTypeMetadata *targetType,
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
DynamicCastFlags flags);
static bool _dynamicCastClassToValueViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
const _ObjectiveCBridgeableWitnessTable *targetBridgeWitness,
DynamicCastFlags flags);
/// A convenient method for failing out of a dynamic cast. /// A convenient method for failing out of a dynamic cast.
static bool _fail(OpaqueValue *srcValue, const Metadata *srcType, static bool _fail(OpaqueValue *srcValue, const Metadata *srcType,
const Metadata *targetType, DynamicCastFlags flags) { const Metadata *targetType, DynamicCastFlags flags) {
@@ -255,7 +279,7 @@ static const OpaqueValue *
_dynamicCastToExistential(const OpaqueValue *value, _dynamicCastToExistential(const OpaqueValue *value,
const Metadata *sourceType, const Metadata *sourceType,
const ExistentialTypeMetadata *targetType) { const ExistentialTypeMetadata *targetType) {
for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) { for (unsigned i = 0, n = targetType->Protocols.NumProtocols; i != n; ++i) {
auto *protocol = targetType->Protocols[i]; auto *protocol = targetType->Protocols[i];
if (!_conformsToProtocol(value, sourceType, protocol, nullptr)) if (!_conformsToProtocol(value, sourceType, protocol, nullptr))
return nullptr; return nullptr;
@@ -407,11 +431,59 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
auto destExistential = auto destExistential =
reinterpret_cast<ClassExistentialContainer*>(dest); reinterpret_cast<ClassExistentialContainer*>(dest);
// If the source type is a value type, it cannot possibly conform
// to a class-bounded protocol.
switch (srcDynamicType->getKind()) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::Existential:
case MetadataKind::ExistentialMetatype:
case MetadataKind::Metatype:
// Handle these cases below.
break;
case MetadataKind::Struct:
case MetadataKind::Enum:
// If the source type is bridged to Objective-C, try to bridge.
if (auto srcBridgeWitness = findBridgeWitness(srcDynamicType)) {
DynamicCastFlags subFlags
= flags - (DynamicCastFlags::TakeOnSuccess |
DynamicCastFlags::DestroyOnFailure);
bool success = _dynamicCastValueToClassExistentialViaObjCBridgeable(
dest,
srcDynamicValue,
srcDynamicType,
targetType,
srcBridgeWitness,
subFlags);
if (src != srcDynamicValue && shouldDeallocateSource(success, flags)) {
deallocateDynamicValue(src, srcType);
}
return success;
}
break;
case MetadataKind::Function:
case MetadataKind::Block:
case MetadataKind::HeapArray:
case MetadataKind::HeapLocalVariable:
case MetadataKind::Opaque:
case MetadataKind::PolyFunction:
case MetadataKind::Tuple:
// Will never succeed.
return _fail(src, srcType, targetType, flags);
}
// Check for protocol conformances and fill in the witness tables. // Check for protocol conformances and fill in the witness tables.
if (!_conformsToProtocols(srcDynamicValue, srcDynamicType, if (!_conformsToProtocols(srcDynamicValue, srcDynamicType,
targetType->Protocols, targetType->Protocols,
destExistential->getWitnessTables())) destExistential->getWitnessTables())) {
return _fail(src, srcType, targetType, flags); return _fail(src, srcType, targetType, flags);
}
auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue)); auto object = *(reinterpret_cast<HeapObject**>(srcDynamicValue));
destExistential->Value = object; destExistential->Value = object;
@@ -1008,22 +1080,6 @@ static bool _dynamicCastToExistentialMetatype(OpaqueValue *dest,
_failCorruptType(srcType); _failCorruptType(srcType);
} }
static bool _dynamicCastValueToClassViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
DynamicCastFlags flags);
static bool _dynamicCastClassToValueViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const Metadata *targetType,
const _ObjectiveCBridgeableWitnessTable *targetBridgeWitness,
DynamicCastFlags flags);
/// Perform a dynamic cast to an arbitrary type. /// Perform a dynamic cast to an arbitrary type.
bool swift::swift_dynamicCast(OpaqueValue *dest, bool swift::swift_dynamicCast(OpaqueValue *dest,
OpaqueValue *src, OpaqueValue *src,
@@ -1665,7 +1721,7 @@ extern "C" const ProtocolDescriptor _TMpSs21_ObjectiveCBridgeable;
/// Dynamic cast from a value type that conforms to the _ObjectiveCBridgeable /// 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 /// protocol to a class type, first by bridging the value to its Objective-C
/// object represntation and then by dynamic casting that object to the /// object representation and then by dynamic casting that object to the
/// resulting target type. /// resulting target type.
static bool _dynamicCastValueToClassViaObjCBridgeable( static bool _dynamicCastValueToClassViaObjCBridgeable(
OpaqueValue *dest, OpaqueValue *dest,
@@ -1682,16 +1738,57 @@ static bool _dynamicCastValueToClassViaObjCBridgeable(
// Bridge the source value to an object. // Bridge the source value to an object.
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType); auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
// Dynamic cast the object to the resulting class type. We pass on // Dynamic cast the object to the resulting class type. The
// responsibility // additional flags essneitally make this call act as taking the
// source object at +1.
DynamicCastFlags classCastFlags = flags | DynamicCastFlags::TakeOnSuccess DynamicCastFlags classCastFlags = flags | DynamicCastFlags::TakeOnSuccess
| DynamicCastFlags::DestroyOnFailure; | DynamicCastFlags::DestroyOnFailure;
bool success = _dynamicCastUnknownClass(dest, srcBridgedObject, targetType, bool success = _dynamicCastUnknownClass(dest, srcBridgedObject, targetType,
classCastFlags); classCastFlags);
// Clean up the source if we're supposed to. // Clean up the source if we're supposed to.
if ((success && (flags & DynamicCastFlags::TakeOnSuccess)) || if (shouldDeallocateSource(success, flags)) {
(!success && (flags & DynamicCastFlags::DestroyOnFailure))) { srcType->vw_destroy(src);
}
// We're done.
return success;
}
/// Dynamic cast from a value type that conforms to the
/// _ObjectiveCBridgeable protocol to a class-bounded existential,
/// first by bridging the value to its Objective-C object
/// representation and then by dynamic-casting that object to the
/// resulting target type.
static bool _dynamicCastValueToClassExistentialViaObjCBridgeable(
OpaqueValue *dest,
OpaqueValue *src,
const Metadata *srcType,
const ExistentialTypeMetadata *targetType,
const _ObjectiveCBridgeableWitnessTable *srcBridgeWitness,
DynamicCastFlags flags) {
// Check whether the source is bridged to Objective-C.
if (!srcBridgeWitness->isBridgedToObjectiveC(srcType, srcType)) {
return _fail(src, srcType, targetType, flags);
}
// Bridge the source value to an object.
auto srcBridgedObject = srcBridgeWitness->bridgeToObjectiveC(src, srcType);
// Try to cast the object to the destination existential.
DynamicCastFlags subFlags = DynamicCastFlags::TakeOnSuccess
| DynamicCastFlags::DestroyOnFailure;
if (flags & DynamicCastFlags::Unconditional)
subFlags |= DynamicCastFlags::Unconditional;
bool success = _dynamicCastToExistential(
dest,
(OpaqueValue *)&srcBridgedObject,
swift_getObjectType(srcBridgedObject),
targetType,
subFlags);
// Clean up the source if we're supposed to.
if (shouldDeallocateSource(success, flags)) {
srcType->vw_destroy(src); srcType->vw_destroy(src);
} }

View File

@@ -32,8 +32,19 @@ func testForcedValueToObjectBridging() {
// CHECK-NEXT: ) // CHECK-NEXT: )
println(genericForcedCast(array) as NSObject) println(genericForcedCast(array) as NSObject)
// FIXME: Forced bridging (AnyObject) // Forced bridging (AnyObject)
// FIXME: Forced bridging (existential success) // CHECK-NEXT: (
// CHECK-NEXT: Hello,
// CHECK-NEXT: World
// CHECK-NEXT: )
println(genericForcedCast(array) as NSObject)
// Forced bridging (existential success)
// CHECK-NEXT: (
// CHECK-NEXT: Hello,
// CHECK-NEXT: World
// CHECK-NEXT: )
println(genericForcedCast(array) as NSCoding)
println("Done") println("Done")
} }
@@ -68,9 +79,34 @@ func testConditionalValueToObjectBridging() {
println("Not an NSObject") println("Not an NSObject")
} }
// FIXME: Conditional bridging (AnyObject) // Conditional bridging (AnyObject)
// FIXME: Conditional bridging (existential success) // CHECK-NEXT: (
// FIXME: Conditional bridging (existential failure) // CHECK-NEXT: Hello,
// CHECK-NEXT: World
// CHECK-NEXT: )
if let anyObject = (genericConditionalCast(array) as AnyObject?) {
println("\(anyObject)")
} else {
println("Not an AnyObject")
}
// Conditional bridging (existential success)
// CHECK-NEXT: (
// CHECK-NEXT: Hello,
// CHECK-NEXT: World
// CHECK-NEXT: )
if let coding = (genericConditionalCast(array) as NSCoding?) {
println("\(coding)")
} else {
println("Not an NSCoding")
}
// CHECK-NEXT: NSXMLParserDelegate
if let delegate = (genericConditionalCast(array) as NSXMLParserDelegate?) {
println("\(delegate)")
} else {
println("Not an NSXMLParserDelegate")
}
// Conditional bridging (unrelated class) // Conditional bridging (unrelated class)
// CHECK-NEXT: Not an NSString // CHECK-NEXT: Not an NSString