Skip to content

Commit 905c830

Browse files
committed
[Runtime] Handle Error bridging as a last chance to cast to NSError/NSObject.
Rather than attempting Error bridging early when trying to dynamically cast to NSError or NSObject, treat it as the *last* thing we do when all else fails. Push most of this code over into Objective-C-specific handling rather than #ifdef'd into the main casting logic to make that slightly more clear. One oddity of Error/NSError bridging is that a class that conforms to Error can be dynamically cast to NSObject via Error bridging. This has always been known to the static compiler, but the runtime itself was not always handling such a cast uniformly. Do so now, uniformly. However, this forced us to weaken an assertion, because casting a class type to NSError or NSObject can produce an object with a different identity. Fixes rdar://problem/57393991.
1 parent 8261d6f commit 905c830

File tree

5 files changed

+95
-77
lines changed

5 files changed

+95
-77
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 18 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -588,15 +588,6 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype,
588588
}
589589
}
590590

591-
// internal func _getErrorEmbeddedNSErrorIndirect<T : Error>(
592-
// _ x: UnsafePointer<T>) -> AnyObject?
593-
#define getErrorEmbeddedNSErrorIndirect \
594-
MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF)
595-
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
596-
id getErrorEmbeddedNSErrorIndirect(const OpaqueValue *error,
597-
const Metadata *T,
598-
const WitnessTable *Error);
599-
600591
#endif
601592

602593
/******************************************************************************/
@@ -1256,7 +1247,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest,
12561247
// Okay, we're doing a conditional cast.
12571248
void *result =
12581249
const_cast<void*>(swift_dynamicCastUnknownClass(object, targetType));
1259-
assert(result == nullptr || object == result);
12601250

12611251
// If the cast failed, destroy the input and return false.
12621252
if (!result) {
@@ -1278,14 +1268,6 @@ static bool _dynamicCastUnknownClassIndirect(OpaqueValue *dest,
12781268
/******************************** Existentials ********************************/
12791269
/******************************************************************************/
12801270

1281-
#if SWIFT_OBJC_INTEROP
1282-
extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
1283-
1284-
static const WitnessTable *findErrorWitness(const Metadata *srcType) {
1285-
return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error));
1286-
}
1287-
#endif
1288-
12891271
/// Perform a dynamic cast from an existential type to some kind of
12901272
/// class type.
12911273
static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest,
@@ -1298,15 +1280,6 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest,
12981280
auto classContainer =
12991281
reinterpret_cast<ClassExistentialContainer*>(src);
13001282
void *obj = classContainer->Value;
1301-
#if SWIFT_OBJC_INTEROP
1302-
// If we're casting to NSError, we may need a representation change,
1303-
// so fall into the general swift_dynamicCast path.
1304-
if (targetType == getNSErrorMetadata() ||
1305-
targetType == getNSObjectMetadata()) {
1306-
return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj),
1307-
targetType, flags);
1308-
}
1309-
#endif
13101283
return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags);
13111284
}
13121285
case ExistentialTypeRepresentation::Opaque: {
@@ -1815,32 +1788,6 @@ static bool _dynamicCastToFunction(OpaqueValue *dest,
18151788
}
18161789
}
18171790

1818-
/******************************************************************************/
1819-
/****************************** Bridging NSError ******************************/
1820-
/******************************************************************************/
1821-
1822-
#if SWIFT_OBJC_INTEROP
1823-
static id dynamicCastValueToNSError(OpaqueValue *src,
1824-
const Metadata *srcType,
1825-
const WitnessTable *srcErrorWitness,
1826-
DynamicCastFlags flags) {
1827-
// Check whether there is an embedded NSError.
1828-
if (auto embedded = getErrorEmbeddedNSErrorIndirect(src, srcType,
1829-
srcErrorWitness)) {
1830-
if (flags & DynamicCastFlags::TakeOnSuccess)
1831-
srcType->vw_destroy(src);
1832-
1833-
return embedded;
1834-
}
1835-
1836-
BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src,
1837-
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
1838-
auto *error = (SwiftError *)errorBox.object;
1839-
return _swift_stdlib_bridgeErrorToNSError(error);
1840-
}
1841-
1842-
#endif
1843-
18441791
/******************************************************************************/
18451792
/********************************* Optionals **********************************/
18461793
/******************************************************************************/
@@ -2333,26 +2280,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src,
23332280
// Casts to class type.
23342281
case MetadataKind::Class:
23352282
case MetadataKind::ObjCClassWrapper:
2336-
#if SWIFT_OBJC_INTEROP
2337-
// If the destination type is an NSError or NSObject, and the source type
2338-
// is an Error, then the cast can succeed by NSError bridging.
2339-
if (targetType == getNSErrorMetadata() ||
2340-
targetType == getNSObjectMetadata()) {
2341-
// Don't rebridge if the source is already some kind of NSError.
2342-
if (srcType->isAnyClass()
2343-
&& swift_dynamicCastObjCClass(*reinterpret_cast<id*>(src),
2344-
static_cast<const ObjCClassWrapperMetadata*>(targetType)->Class))
2345-
return _succeed(dest, src, srcType, flags);
2346-
if (auto srcErrorWitness = findErrorWitness(srcType)) {
2347-
auto error = dynamicCastValueToNSError(src, srcType,
2348-
srcErrorWitness, flags);
2349-
*reinterpret_cast<id *>(dest) = error;
2350-
return true;
2351-
}
2352-
}
2353-
LLVM_FALLTHROUGH;
2354-
#endif
2355-
23562283
case MetadataKind::ForeignClass:
23572284
switch (srcType->getKind()) {
23582285
case MetadataKind::Class:
@@ -2388,6 +2315,21 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src,
23882315
srcBridgeWitness,
23892316
flags);
23902317
}
2318+
2319+
#if SWIFT_OBJC_INTEROP
2320+
// If the destination type is an NSError or NSObject, and the source type
2321+
// is an Error, then the cast can succeed by NSError bridging.
2322+
if (targetType == getNSErrorMetadata() ||
2323+
targetType == getNSObjectMetadata()) {
2324+
if (auto srcErrorWitness = findErrorWitness(srcType)) {
2325+
auto error = dynamicCastValueToNSError(src, srcType,
2326+
srcErrorWitness, flags);
2327+
*reinterpret_cast<id *>(dest) = error;
2328+
return true;
2329+
}
2330+
}
2331+
#endif
2332+
23912333
return _fail(src, srcType, targetType, flags);
23922334
}
23932335

@@ -2847,9 +2789,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
28472789
// Handle Errors.
28482790
} else if (auto srcErrorWitness = findErrorWitness(srcType)) {
28492791
// Bridge the source value to an NSError.
2850-
auto box = swift_allocError(srcType, srcErrorWitness, src, consume)
2851-
.object;
2852-
return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box);
2792+
auto flags = consume ? DynamicCastFlags::TakeOnSuccess
2793+
: DynamicCastFlags::Default;
2794+
return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags);
28532795
}
28542796

28552797
// Fall back to boxing.

stdlib/public/runtime/ErrorObject.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ Class getNSErrorClass();
253253
/// Get the NSError metadata.
254254
const Metadata *getNSErrorMetadata();
255255

256+
/// Find the witness table for the conformance of the given type to the
257+
/// Error protocol, or return nullptr if it does not conform.
258+
const WitnessTable *findErrorWitness(const Metadata *srcType);
259+
260+
/// Dynamically cast a value whose conformance to the Error protocol is known
261+
/// into an NSError instance.
262+
id dynamicCastValueToNSError(OpaqueValue *src,
263+
const Metadata *srcType,
264+
const WitnessTable *srcErrorWitness,
265+
DynamicCastFlags flags);
266+
256267
#endif
257268

258269
SWIFT_RUNTIME_STDLIB_SPI
@@ -263,4 +274,15 @@ const size_t _swift_lldb_sizeof_SwiftError;
263274

264275
} // namespace swift
265276

277+
#if SWIFT_OBJC_INTEROP
278+
// internal func _getErrorEmbeddedNSErrorIndirect<T : Error>(
279+
// _ x: UnsafePointer<T>) -> AnyObject?
280+
#define getErrorEmbeddedNSErrorIndirect \
281+
MANGLE_SYM(s32_getErrorEmbeddedNSErrorIndirectyyXlSgSPyxGs0B0RzlF)
282+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
283+
id getErrorEmbeddedNSErrorIndirect(const swift::OpaqueValue *error,
284+
const swift::Metadata *T,
285+
const swift::WitnessTable *Error);
286+
#endif
287+
266288
#endif

stdlib/public/runtime/ErrorObject.mm

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,31 @@ - (BOOL)isEqual:(id)other {
178178
swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass()));
179179
}
180180

181+
extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
182+
183+
const WitnessTable *swift::findErrorWitness(const Metadata *srcType) {
184+
return swift_conformsToProtocol(srcType, &PROTOCOL_DESCR_SYM(s5Error));
185+
}
186+
187+
id swift::dynamicCastValueToNSError(OpaqueValue *src,
188+
const Metadata *srcType,
189+
const WitnessTable *srcErrorWitness,
190+
DynamicCastFlags flags) {
191+
// Check whether there is an embedded NSError.
192+
if (id embedded = getErrorEmbeddedNSErrorIndirect(src, srcType,
193+
srcErrorWitness)) {
194+
if (flags & DynamicCastFlags::TakeOnSuccess)
195+
srcType->vw_destroy(src);
196+
197+
return embedded;
198+
}
199+
200+
BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src,
201+
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
202+
auto *error = (SwiftError *)errorBox.object;
203+
return _swift_stdlib_bridgeErrorToNSError(error);
204+
}
205+
181206
static Class getAndBridgeSwiftNativeNSErrorClass() {
182207
Class nsErrorClass = swift::getNSErrorClass();
183208
Class ourClass = [__SwiftNativeNSError class];

stdlib/public/runtime/SwiftObject.mm

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "../SwiftShims/RuntimeShims.h"
3636
#include "../SwiftShims/AssertionReporting.h"
3737
#include "CompatibilityOverride.h"
38+
#include "ErrorObject.h"
3839
#include "Private.h"
3940
#include "SwiftObject.h"
4041
#include "WeakReference.h"
@@ -1098,6 +1099,20 @@ static bool isObjCForUnownedReference(void *value) {
10981099
return object;
10991100
}
11001101

1102+
// For casts to NSError or NSObject, we might need to bridge via the Error
1103+
// protocol. Try it now.
1104+
if (targetType == reinterpret_cast<const ClassMetadata*>(getNSErrorClass()) ||
1105+
targetType == reinterpret_cast<const ClassMetadata*>([NSObject class])) {
1106+
auto srcType = swift_getObjCClassMetadata(
1107+
reinterpret_cast<const ClassMetadata*>(
1108+
object_getClass(id_const_cast(object))));
1109+
if (auto srcErrorWitness = findErrorWitness(srcType)) {
1110+
return dynamicCastValueToNSError((OpaqueValue*)&object, srcType,
1111+
srcErrorWitness,
1112+
DynamicCastFlags::TakeOnSuccess);
1113+
}
1114+
}
1115+
11011116
return nullptr;
11021117
}
11031118

@@ -1114,6 +1129,20 @@ static bool isObjCForUnownedReference(void *value) {
11141129
return object;
11151130
}
11161131

1132+
// For casts to NSError or NSObject, we might need to bridge via the Error
1133+
// protocol. Try it now.
1134+
if (targetType == reinterpret_cast<const ClassMetadata*>(getNSErrorClass()) ||
1135+
targetType == reinterpret_cast<const ClassMetadata*>([NSObject class])) {
1136+
auto srcType = swift_getObjCClassMetadata(
1137+
reinterpret_cast<const ClassMetadata*>(
1138+
object_getClass(id_const_cast(object))));
1139+
if (auto srcErrorWitness = findErrorWitness(srcType)) {
1140+
return dynamicCastValueToNSError((OpaqueValue*)&object, srcType,
1141+
srcErrorWitness,
1142+
DynamicCastFlags::TakeOnSuccess);
1143+
}
1144+
}
1145+
11171146
Class sourceType = object_getClass(id_const_cast(object));
11181147
swift_dynamicCastFailure(reinterpret_cast<const Metadata *>(sourceType),
11191148
targetType);

test/stdlib/BridgeIdAsAny.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ testCases = [
201201

202202
/// Whether this can be safely casted to NSObject
203203
func isNSObject<T>(_ value: T) -> Bool {
204-
return (value as? NSObject) != nil
204+
return (value is NSObject) && !(value is LifetimeTracked)
205205
}
206206

207207
% for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:

0 commit comments

Comments
 (0)