Skip to content

Commit 6cc1337

Browse files
authored
Merge pull request swiftlang#28700 from DougGregor/bridge-all-the-errors
[Runtime] Handle Error-conforming-to-NSObject casting fully.
2 parents 3780479 + 169dbcd commit 6cc1337

File tree

6 files changed

+108
-82
lines changed

6 files changed

+108
-82
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 18 additions & 75 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,14 +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-
return swift_dynamicCast(dest, src, swift_getObjectType((HeapObject*)obj),
1306-
targetType, flags);
1307-
}
1308-
#endif
13091283
return _dynamicCastUnknownClassIndirect(dest, obj, targetType, flags);
13101284
}
13111285
case ExistentialTypeRepresentation::Opaque: {
@@ -1814,32 +1788,6 @@ static bool _dynamicCastToFunction(OpaqueValue *dest,
18141788
}
18151789
}
18161790

1817-
/******************************************************************************/
1818-
/****************************** Bridging NSError ******************************/
1819-
/******************************************************************************/
1820-
1821-
#if SWIFT_OBJC_INTEROP
1822-
static id dynamicCastValueToNSError(OpaqueValue *src,
1823-
const Metadata *srcType,
1824-
const WitnessTable *srcErrorWitness,
1825-
DynamicCastFlags flags) {
1826-
// Check whether there is an embedded NSError.
1827-
if (auto embedded = getErrorEmbeddedNSErrorIndirect(src, srcType,
1828-
srcErrorWitness)) {
1829-
if (flags & DynamicCastFlags::TakeOnSuccess)
1830-
srcType->vw_destroy(src);
1831-
1832-
return embedded;
1833-
}
1834-
1835-
BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src,
1836-
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
1837-
auto *error = (SwiftError *)errorBox.object;
1838-
return _swift_stdlib_bridgeErrorToNSError(error);
1839-
}
1840-
1841-
#endif
1842-
18431791
/******************************************************************************/
18441792
/********************************* Optionals **********************************/
18451793
/******************************************************************************/
@@ -2332,26 +2280,6 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src,
23322280
// Casts to class type.
23332281
case MetadataKind::Class:
23342282
case MetadataKind::ObjCClassWrapper:
2335-
#if SWIFT_OBJC_INTEROP
2336-
// If the destination type is an NSError or NSObject, and the source type
2337-
// is an Error, then the cast can succeed by NSError bridging.
2338-
if (targetType == getNSErrorMetadata() ||
2339-
targetType == getNSObjectMetadata()) {
2340-
// Don't rebridge if the source is already some kind of NSError.
2341-
if (srcType->isAnyClass()
2342-
&& swift_dynamicCastObjCClass(*reinterpret_cast<id*>(src),
2343-
static_cast<const ObjCClassWrapperMetadata*>(targetType)->Class))
2344-
return _succeed(dest, src, srcType, flags);
2345-
if (auto srcErrorWitness = findErrorWitness(srcType)) {
2346-
auto error = dynamicCastValueToNSError(src, srcType,
2347-
srcErrorWitness, flags);
2348-
*reinterpret_cast<id *>(dest) = error;
2349-
return true;
2350-
}
2351-
}
2352-
LLVM_FALLTHROUGH;
2353-
#endif
2354-
23552283
case MetadataKind::ForeignClass:
23562284
switch (srcType->getKind()) {
23572285
case MetadataKind::Class:
@@ -2387,6 +2315,21 @@ static bool swift_dynamicCastImpl(OpaqueValue *dest, OpaqueValue *src,
23872315
srcBridgeWitness,
23882316
flags);
23892317
}
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+
23902333
return _fail(src, srcType, targetType, flags);
23912334
}
23922335

@@ -2846,9 +2789,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
28462789
// Handle Errors.
28472790
} else if (auto srcErrorWitness = findErrorWitness(srcType)) {
28482791
// Bridge the source value to an NSError.
2849-
auto box = swift_allocError(srcType, srcErrorWitness, src, consume)
2850-
.object;
2851-
return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box);
2792+
auto flags = consume ? DynamicCastFlags::TakeOnSuccess
2793+
: DynamicCastFlags::Default;
2794+
return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags);
28522795
}
28532796

28542797
// 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: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,7 @@ protocol P {}
175175
%{
176176
testCases = [
177177
# testName type valueExpr testFunc conformsToError conformsToHashable
178-
179-
# disabled to unblock CI: rdar://problem/57393991
180-
# ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
181-
178+
("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
182179
("strings", "String", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True),
183180
("unbridged type", "KnownUnbridged", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", False, True),
184181
("tuple", "(Int, String)", '(1, "2")', "tupleCanBeDynamicallyCast", False, False),
@@ -199,6 +196,11 @@ testCases = [
199196
]
200197
}%
201198

199+
/// Whether this can be safely casted to NSObject
200+
func isNSObject<T>(_ value: T) -> Bool {
201+
return (value is NSObject) && !(value is LifetimeTracked)
202+
}
203+
202204
% for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:
203205
BridgeAnything.test("${testName}") {
204206
autoreleasepool {
@@ -210,7 +212,7 @@ BridgeAnything.test("${testName}") {
210212
let xInArray = [x]
211213
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
212214
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
213-
if (x as? NSObject) != nil {
215+
if isNSObject(x) {
214216
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
215217
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
216218
}
@@ -219,7 +221,7 @@ BridgeAnything.test("${testName}") {
219221
let xInDictValue = ["key" : x]
220222
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!)
221223
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!)
222-
if (x as? NSObject) != nil {
224+
if isNSObject(x) {
223225
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!)
224226
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!)
225227
}
@@ -231,7 +233,7 @@ BridgeAnything.test("${testName}") {
231233
// The NSObject version below can't test class LifetimeTracked.
232234
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!)
233235
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!)
234-
if (x as? NSObject) != nil {
236+
if isNSObject(x) {
235237
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!)
236238
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!)
237239
}

test/stdlib/ErrorBridged.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,11 @@ ErrorBridgingTests.test("error-to-NSObject casts") {
785785

786786
// "is" check
787787
expectTrue(error is NSObject)
788+
789+
// Unconditional cast to a dictionary.
790+
let dict = ["key" : NoisyError()]
791+
let anyOfDict = dict as AnyObject
792+
let dict2 = anyOfDict as! [String: NSObject]
788793
}
789794
}
790795

0 commit comments

Comments
 (0)