Skip to content

Commit 78b5ff8

Browse files
committed
Runtime: Bridge Error-conforming types to id as NSError instances.
NSError is a more useful box for a swift Error than the generic box. Fixes rdar://problem/38631791 | SR-7232.
1 parent 88b785c commit 78b5ff8

File tree

3 files changed

+134
-47
lines changed

3 files changed

+134
-47
lines changed

stdlib/public/runtime/Casting.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -2959,6 +2959,7 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
29592959
return objc_retain(protocolObj);
29602960
}
29612961
}
2962+
// Handle bridgable types.
29622963
} else if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
29632964
// Bridge the source value to an object.
29642965
auto srcBridgedObject =
@@ -2969,6 +2970,12 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
29692970
srcType->vw_destroy(src);
29702971

29712972
return (id)srcBridgedObject;
2973+
// Handle Errors.
2974+
} else if (auto srcErrorWitness = findErrorWitness(srcType)) {
2975+
// Bridge the source value to an NSError.
2976+
auto box = swift_allocError(srcType, srcErrorWitness, src, consume)
2977+
.object;
2978+
return _swift_stdlib_bridgeErrorToNSError((SwiftError*)box);
29722979
}
29732980

29742981
// Fall back to boxing.

stdlib/public/runtime/ErrorObject.mm

+35-18
Original file line numberDiff line numberDiff line change
@@ -457,32 +457,18 @@ typedef SWIFT_CC(swift)
457457
return ns;
458458
}
459459

460+
extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
461+
460462
bool
461463
swift::tryDynamicCastNSErrorToValue(OpaqueValue *dest,
462464
OpaqueValue *src,
463465
const Metadata *srcType,
464466
const Metadata *destType,
465467
DynamicCastFlags flags) {
466468
Class NSErrorClass = getNSErrorClass();
467-
468469
auto CFErrorTypeID = SWIFT_LAZY_CONSTANT(CFErrorGetTypeID());
469-
// public func Foundation._bridgeNSErrorToError<
470-
// T : _ObjectiveCBridgeableError
471-
// >(error: NSError, out: UnsafeMutablePointer<T>) -> Bool {
472-
typedef SWIFT_CC(swift)
473-
bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
474-
const WitnessTable *);
475-
auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT(
476-
reinterpret_cast<BridgeFn*>(dlsym(RTLD_DEFAULT,
477-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)))));
478-
// protocol _ObjectiveCBridgeableError
479-
auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT(
480-
reinterpret_cast<const ProtocolDescriptor *>(dlsym(RTLD_DEFAULT,
481-
MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp)))));
482470

483-
// If the Foundation overlay isn't loaded, then NSErrors can't be bridged.
484-
if (!bridgeNSErrorToError || !TheObjectiveCBridgeableError)
485-
return false;
471+
NSError *srcInstance;
486472

487473
// Is the input type an NSError?
488474
switch (srcType->getKind()) {
@@ -491,6 +477,19 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
491477
// Native class or ObjC class should be an NSError subclass.
492478
if (![srcType->getObjCClassObject() isSubclassOfClass: NSErrorClass])
493479
return false;
480+
481+
srcInstance = *reinterpret_cast<NSError * const*>(src);
482+
483+
// A _SwiftNativeNSError box can always be unwrapped to cast the value back
484+
// out as an Error existential.
485+
if (!reinterpret_cast<SwiftError*>(srcInstance)->isPureNSError()) {
486+
auto theErrorProtocol = &PROTOCOL_DESCR_SYM(s5Error);
487+
auto theErrorTy =
488+
swift_getExistentialTypeMetadata(ProtocolClassConstraint::Any,
489+
nullptr, 1, &theErrorProtocol);
490+
return swift_dynamicCast(dest, src, theErrorTy, destType, flags);
491+
}
492+
494493
break;
495494
case MetadataKind::ForeignClass: {
496495
// Foreign class should be CFError.
@@ -515,6 +514,25 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
515514
return false;
516515
}
517516

517+
// public func Foundation._bridgeNSErrorToError<
518+
// T : _ObjectiveCBridgeableError
519+
// >(error: NSError, out: UnsafeMutablePointer<T>) -> Bool {
520+
typedef SWIFT_CC(swift)
521+
bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
522+
const WitnessTable *);
523+
auto bridgeNSErrorToError = SWIFT_LAZY_CONSTANT(
524+
reinterpret_cast<BridgeFn*>(dlsym(RTLD_DEFAULT,
525+
MANGLE_AS_STRING(MANGLE_SYM(10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF)))));
526+
// protocol _ObjectiveCBridgeableError
527+
auto TheObjectiveCBridgeableError = SWIFT_LAZY_CONSTANT(
528+
reinterpret_cast<const ProtocolDescriptor *>(dlsym(RTLD_DEFAULT,
529+
MANGLE_AS_STRING(MANGLE_SYM(10Foundation26_ObjectiveCBridgeableErrorMp)))));
530+
531+
// If the Foundation overlay isn't loaded, then arbitrary NSErrors can't be
532+
// bridged.
533+
if (!bridgeNSErrorToError || !TheObjectiveCBridgeableError)
534+
return false;
535+
518536
// Is the target type a bridgeable error?
519537
auto witness = swift_conformsToProtocol(destType,
520538
TheObjectiveCBridgeableError);
@@ -523,7 +541,6 @@ bool BridgeFn(NSError *, OpaqueValue*, const Metadata *,
523541
return false;
524542

525543
// If so, attempt the bridge.
526-
NSError *srcInstance = *reinterpret_cast<NSError * const*>(src);
527544
SWIFT_CC_PLUSONE_GUARD(objc_retain(srcInstance));
528545
if (bridgeNSErrorToError(srcInstance, dest, destType, witness)) {
529546
if (flags & DynamicCastFlags::TakeOnSuccess)

test/stdlib/BridgeIdAsAny.swift.gyb

+92-29
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func wantonlyWrapInAny<T>(_ x: T) -> Any {
2020
extension LifetimeTracked: Error {}
2121
extension String: Error {}
2222

23-
struct KnownUnbridged: Equatable, Hashable, Error {
23+
struct KnownUnbridged: Equatable, Hashable {
2424
var x, y: LifetimeTracked
2525

2626
init() {
@@ -149,38 +149,55 @@ func protocolObjectPreservesIdentity(original: NSCopying.Protocol,
149149
expectTrue(proto === bridged)
150150
}
151151

152+
enum MyError: Error {
153+
case a, e, i, o, u
154+
}
155+
156+
func errorValueTypeBridgesToNSError(original: MyError, bridged: AnyObject) {
157+
let bridgedNSError = bridged as! NSError
158+
expectEqual(bridgedNSError.domain, original._domain)
159+
expectEqual(bridgedNSError.code, original._code)
160+
161+
let unbridgedError = bridged as! MyError
162+
expectEqual(original, unbridgedError)
163+
164+
let unbridgedError2 = bridgedNSError as! MyError
165+
expectEqual(original, unbridgedError2)
166+
}
167+
152168
protocol P {}
153169

154170
// We want to exhaustively check all paths through the bridging and dynamic
155171
// casting infrastructure, so expand out test cases that wrap the different
156172
// interesting bridging cases in different kinds of existential container.
157173
%{
158174
testCases = [
159-
# testName valueExpr testFunc conformsToError conformsToHashable
160-
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
161-
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True),
162-
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True, True),
163-
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False, False),
164-
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False, False),
165-
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
166-
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False, False),
167-
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
168-
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False, False),
169-
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False, False),
170-
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
171-
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False, False),
172-
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
173-
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
174-
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False, False),
175-
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False, False),
176-
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False, False),
175+
# testName type valueExpr testFunc conformsToError conformsToHashable
176+
("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
177+
("strings", "String", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True),
178+
("unbridged type", "KnownUnbridged", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", False, True),
179+
("tuple", "(Int, String)", '(1, "2")', "tupleCanBeDynamicallyCast", False, False),
180+
("metatype", "Int.Type", 'Int.self', "metatypeCanBeDynamicallyCast", False, False),
181+
("generic metatype", "Int.Type", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
182+
("CF metatype", "CFString.Type", 'CFString.self', "metatypeCanBeDynamicallyCast", False, False),
183+
("generic CF metatype", "CFString.Type", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
184+
("class metatype", "LifetimeTracked.Type", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False, False),
185+
("objc metatype", "NSObject.Type", 'NSObject.self', "classMetatypePreservesIdentity", False, False),
186+
("protocol", "P.Protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
187+
("objc protocol", "NSCopying.Protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False, False),
188+
("objc protocol composition", "(NSCopying & NSCoding).Protocol", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
189+
("mixed protocol composition", "(NSCopying & P).Protocol", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
190+
("generic class metatype", "LifetimeTracked.Type", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False, False),
191+
("generic objc metatype", "NSObject.Type", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False, False),
192+
("function", "() -> Int", 'guineaPigFunction', "functionCanBeDynamicallyCast", False, False),
193+
("error type", "MyError", 'MyError.e', "errorValueTypeBridgesToNSError", True, True),
177194
]
178195
}%
179196

180-
% for testName, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:
197+
% for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:
181198
BridgeAnything.test("${testName}") {
182199
autoreleasepool {
183-
let x = ${valueExpr}
200+
let x: ${type} = ${valueExpr}
184201
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(x))
185202
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(x))
186203

@@ -217,21 +234,67 @@ BridgeAnything.test("${testName}") {
217234
% end
218235

219236
let xInAny: Any = x
220-
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInAny))
221-
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInAny))
237+
let xInAnyBridged = _bridgeAnythingToObjectiveC(xInAny)
238+
let xInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInAny)
239+
${testFunc}(original: x, bridged: xInAnyBridged)
240+
${testFunc}(original: x, bridged: xInAnyBridged2)
222241

223242
let xInAnyInAny = wantonlyWrapInAny(xInAny)
224-
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInAnyInAny))
225-
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInAnyInAny))
243+
let xInAnyInAnyBridged = _bridgeAnythingToObjectiveC(xInAnyInAny)
244+
let xInAnyInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInAnyInAny)
245+
${testFunc}(original: x, bridged: xInAnyInAnyBridged)
246+
${testFunc}(original: x, bridged: xInAnyInAnyBridged2)
226247

227248
% if conformsToError:
228249
let xInError: Error = x
229-
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInError))
230-
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInError))
250+
let xInErrorBridged = _bridgeAnythingToObjectiveC(xInError)
251+
let xInErrorBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInError)
252+
253+
${testFunc}(original: x, bridged: xInErrorBridged)
254+
${testFunc}(original: x, bridged: xInErrorBridged2)
231255

232256
let xInErrorInAny = wantonlyWrapInAny(xInError)
233-
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInErrorInAny))
234-
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInErrorInAny))
257+
let xInErrorInAnyBridged = _bridgeAnythingToObjectiveC(xInErrorInAny)
258+
let xInErrorInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInErrorInAny)
259+
${testFunc}(original: x, bridged: xInErrorInAnyBridged)
260+
${testFunc}(original: x, bridged: xInErrorInAnyBridged)
261+
% end
262+
263+
% if conformsToHashable:
264+
// Check that we get an equal value if we bridge cast back.
265+
let xFromAny = xInAnyBridged as! ${type}
266+
expectEqual(x, xFromAny)
267+
let xFromAny2 = xInAnyBridged2 as! ${type}
268+
expectEqual(x, xFromAny2)
269+
let xInAnyBridgedInAny = wantonlyWrapInAny(xInAnyBridged)
270+
let xFromAnyBridgedInAny = xInAnyBridgedInAny as! ${type}
271+
expectEqual(x, xFromAnyBridgedInAny)
272+
273+
let xFromAnyInAny = xInAnyInAnyBridged as! ${type}
274+
expectEqual(x, xFromAnyInAny)
275+
let xFromAnyInAny2 = xInAnyInAnyBridged2 as! ${type}
276+
expectEqual(x, xFromAnyInAny2)
277+
let xInAnyInAnyBridgedInAny = wantonlyWrapInAny(xInAnyInAnyBridged)
278+
let xFromAnyInAnyBridgedInAny = xInAnyInAnyBridgedInAny as! ${type}
279+
expectEqual(x, xFromAnyInAnyBridgedInAny)
280+
281+
% if conformsToError:
282+
let xFromError = xInErrorBridged as! ${type}
283+
expectEqual(x, xFromError)
284+
let xFromError2 = xInErrorBridged2 as! ${type}
285+
expectEqual(x, xFromError2)
286+
let xInErrorBridgedInAny = wantonlyWrapInAny(xInErrorBridged)
287+
let xFromErrorBridgedInAny = xInErrorBridgedInAny as! ${type}
288+
expectEqual(x, xFromErrorBridgedInAny)
289+
290+
let xFromErrorInAny = xInErrorInAnyBridged as! ${type}
291+
expectEqual(x, xFromErrorInAny)
292+
let xFromErrorInAny2 = xInErrorInAnyBridged2 as! ${type}
293+
expectEqual(x, xFromErrorInAny2)
294+
let xInErrorInAnyBridgedInAny = wantonlyWrapInAny(xInErrorInAnyBridged)
295+
let xFromErrorInAnyBridgedInAny = xInErrorInAnyBridgedInAny as! ${type}
296+
expectEqual(x, xFromErrorInAnyBridgedInAny)
297+
% end
235298
% end
236299
}
237300
}

0 commit comments

Comments
 (0)