Skip to content

Commit 621b826

Browse files
authored
Merge pull request #3953 from DougGregor/sr-1562
2 parents 8436ef8 + d219531 commit 621b826

File tree

8 files changed

+236
-2
lines changed

8 files changed

+236
-2
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,83 @@ SILGenModule::getConformanceToObjectiveCBridgeable(SILLocation loc, Type type) {
283283
return nullptr;
284284
}
285285

286+
ProtocolDecl *SILGenModule::getBridgedStoredNSError(SILLocation loc) {
287+
if (BridgedStoredNSError)
288+
return *BridgedStoredNSError;
289+
290+
// Find the _BridgedStoredNSError protocol.
291+
auto &ctx = getASTContext();
292+
auto proto = ctx.getProtocol(KnownProtocolKind::BridgedStoredNSError);
293+
BridgedStoredNSError = proto;
294+
return proto;
295+
}
296+
297+
VarDecl *SILGenModule::getNSErrorRequirement(SILLocation loc) {
298+
if (NSErrorRequirement)
299+
return *NSErrorRequirement;
300+
301+
// Find the _BridgedStoredNSError protocol.
302+
auto proto = getBridgedStoredNSError(loc);
303+
if (!proto) {
304+
NSErrorRequirement = nullptr;
305+
return nullptr;
306+
}
307+
308+
// Look for _nsError.
309+
auto &ctx = getASTContext();
310+
VarDecl *found = nullptr;
311+
for (auto member : proto->lookupDirect(ctx.Id_nsError, true)) {
312+
if (auto var = dyn_cast<VarDecl>(member)) {
313+
found = var;
314+
break;
315+
}
316+
}
317+
318+
NSErrorRequirement = found;
319+
return found;
320+
}
321+
322+
ProtocolConformance *
323+
SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) {
324+
auto proto = getBridgedStoredNSError(loc);
325+
if (!proto) return nullptr;
326+
327+
// Find the conformance to _BridgedStoredNSError.
328+
auto result = SwiftModule->lookupConformance(type, proto, nullptr);
329+
if (result) return result->getConcrete();
330+
return nullptr;
331+
}
332+
333+
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
334+
if (NSErrorConformanceToError)
335+
return *NSErrorConformanceToError;
336+
337+
auto &ctx = getASTContext();
338+
auto nsError = ctx.getNSErrorDecl();
339+
if (!nsError) {
340+
NSErrorConformanceToError = nullptr;
341+
return nullptr;
342+
}
343+
344+
auto error = ctx.getErrorDecl();
345+
if (!error) {
346+
NSErrorConformanceToError = nullptr;
347+
return nullptr;
348+
}
349+
350+
auto conformance =
351+
SwiftModule->lookupConformance(nsError->getDeclaredInterfaceType(),
352+
cast<ProtocolDecl>(error),
353+
nullptr);
354+
355+
if (conformance && conformance->isConcrete())
356+
NSErrorConformanceToError = conformance->getConcrete();
357+
else
358+
NSErrorConformanceToError = nullptr;
359+
return *NSErrorConformanceToError;
360+
}
361+
362+
286363
SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) {
287364
ASTContext &C = M.getASTContext();
288365
auto extInfo = SILFunctionType::ExtInfo()

lib/SILGen/SILGen.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
129129
Optional<FuncDecl*> UnconditionallyBridgeFromObjectiveCRequirement;
130130
Optional<AssociatedTypeDecl*> BridgedObjectiveCType;
131131

132+
Optional<ProtocolDecl*> BridgedStoredNSError;
133+
Optional<VarDecl*> NSErrorRequirement;
134+
135+
Optional<ProtocolConformance *> NSErrorConformanceToError;
136+
132137
public:
133138
SILGenModule(SILModule &M, Module *SM, bool makeModuleFragile);
134139
~SILGenModule();
@@ -380,6 +385,20 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
380385
ProtocolConformance *getConformanceToObjectiveCBridgeable(SILLocation loc,
381386
Type type);
382387

388+
/// Retrieve the _BridgedStoredNSError protocol definition.
389+
ProtocolDecl *getBridgedStoredNSError(SILLocation loc);
390+
391+
/// Retrieve the _BridgedStoredNSError._nsError requirement.
392+
VarDecl *getNSErrorRequirement(SILLocation loc);
393+
394+
/// Find the conformance of the given Swift type to the
395+
/// _BridgedStoredNSError protocol.
396+
ProtocolConformance *getConformanceToBridgedStoredNSError(SILLocation loc,
397+
Type type);
398+
399+
/// Retrieve the conformance of NSError to the Error protocol.
400+
ProtocolConformance *getNSErrorConformanceToError();
401+
383402
/// Report a diagnostic.
384403
template<typename...T, typename...U>
385404
InFlightDiagnostic diagnose(SourceLoc loc, Diag<T...> diag,

lib/SILGen/SILGenConvert.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,57 @@ ManagedValue SILGenFunction::emitExistentialErasure(
455455
for (auto conformance : conformances)
456456
SGM.useConformance(conformance);
457457

458+
// If we're erasing to the 'Error' type, and the concrete type conforms to the
459+
// _BridgedStoredNSError protocol, call the _nsError witness getter to extract
460+
// the NSError directly.
461+
auto &ctx = getASTContext();
462+
if (existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() ==
463+
ctx.getErrorDecl()) {
464+
auto conformance =
465+
SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType);
466+
auto nsError = ctx.getNSErrorDecl();
467+
if (conformance && nsError && SGM.getNSErrorConformanceToError()) {
468+
CanType nsErrorType =
469+
nsError->getDeclaredInterfaceType()->getCanonicalType();
470+
if (auto witness =
471+
conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) {
472+
// Create a reference to the getter witness.
473+
SILDeclRef getter =
474+
getGetterDeclRef(cast<VarDecl>(witness.getDecl()),
475+
/*isDirectAccessorUse=*/true);
476+
477+
// Compute the substitutions.
478+
ArrayRef<Substitution> substitutions =
479+
concreteFormalType->gatherAllSubstitutions(
480+
SGM.SwiftModule, nullptr);
481+
482+
// Emit the erasure, through the getter to _nsError.
483+
ProtocolConformanceRef nsErrorConformances[1] = {
484+
ProtocolConformanceRef(SGM.getNSErrorConformanceToError())
485+
};
486+
487+
return emitExistentialErasure(
488+
loc, nsErrorType,
489+
getTypeLowering(nsErrorType),
490+
existentialTL,
491+
ctx.AllocateCopy(nsErrorConformances),
492+
C,
493+
[&](SGFContext innerC) -> ManagedValue {
494+
// Call the getter.
495+
return emitGetAccessor(loc, getter, substitutions,
496+
ArgumentSource(loc,
497+
RValue(*this, loc,
498+
concreteFormalType,
499+
F(SGFContext()))),
500+
/*isSuper=*/false,
501+
/*isDirectAccessorUse=*/true,
502+
RValue(), innerC)
503+
.getAsSingleValue(*this, loc);
504+
});
505+
}
506+
}
507+
}
508+
458509
switch (existentialTL.getLoweredType().getObjectType()
459510
.getPreferredExistentialRepresentation(SGM.M, concreteFormalType)) {
460511
case ExistentialRepresentation::None:

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,11 @@ extension _ErrorCodeProtocol where Self._ErrorType: _BridgedStoredNSError {
496496
}
497497

498498
extension _BridgedStoredNSError {
499+
/// Retrieve the embedded NSError from a bridged, stored NSError.
500+
public func _getEmbeddedNSError() -> AnyObject? {
501+
return _nsError
502+
}
503+
499504
public static func == (lhs: Self, rhs: Self) -> Bool {
500505
return lhs._nsError.isEqual(rhs._nsError)
501506
}

stdlib/public/core/ErrorType.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,22 @@ public protocol Error {
114114
var _domain: String { get }
115115
var _code: Int { get }
116116
var _userInfo: Any? { get }
117+
118+
#if _runtime(_ObjC)
119+
func _getEmbeddedNSError() -> AnyObject?
120+
#endif
117121
}
118122

119123
#if _runtime(_ObjC)
120-
// Helper functions for the C++ runtime to have easy access to domain,
121-
// code, and userInfo as Objective-C values.
124+
extension Error {
125+
/// Default implementation: there is no embedded NSError.
126+
public func _getEmbeddedNSError() -> AnyObject? { return nil }
127+
}
128+
#endif
129+
130+
#if _runtime(_ObjC)
131+
// Helper functions for the C++ runtime to have easy access to embedded error,
132+
// domain, code, and userInfo as Objective-C values.
122133
@_silgen_name("swift_stdlib_getErrorDomainNSString")
123134
public func _stdlib_getErrorDomainNSString<T : Error>(_ x: UnsafePointer<T>)
124135
-> AnyObject {
@@ -138,6 +149,12 @@ public func _stdlib_getErrorUserInfoNSDictionary<T : Error>(_ x: UnsafePointer<T
138149
return x.pointee._userInfo.map { $0 as AnyObject }
139150
}
140151

152+
@_silgen_name("swift_stdlib_getErrorEmbeddedNSError")
153+
public func _stdlib_getErrorEmbeddedNSError<T : Error>(_ x: UnsafePointer<T>)
154+
-> AnyObject? {
155+
return x.pointee._getEmbeddedNSError()
156+
}
157+
141158
@_silgen_name("swift_stdlib_getErrorDefaultUserInfo")
142159
public func _stdlib_getErrorDefaultUserInfo(_ error: Error) -> AnyObject?
143160

stdlib/public/runtime/Casting.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,14 +2006,32 @@ static bool _dynamicCastToFunction(OpaqueValue *dest,
20062006
}
20072007

20082008
#if SWIFT_OBJC_INTEROP
2009+
// @_silgen_name("swift_stdlib_getErrorEmbeddedNSError")
2010+
// public func _stdlib_getErrorEmbeddedNSError<T : Error>(_ x: UnsafePointer<T>)
2011+
// -> AnyObject?
2012+
SWIFT_CC(swift)
2013+
extern "C" id swift_stdlib_getErrorEmbeddedNSError(const OpaqueValue *error,
2014+
const Metadata *T,
2015+
const WitnessTable *Error);
2016+
20092017
static id dynamicCastValueToNSError(OpaqueValue *src,
20102018
const Metadata *srcType,
20112019
const WitnessTable *srcErrorWitness,
20122020
DynamicCastFlags flags) {
2021+
// Check whether there is an embedded error.
2022+
if (auto embedded = swift_stdlib_getErrorEmbeddedNSError(src, srcType,
2023+
srcErrorWitness)) {
2024+
if (flags & DynamicCastFlags::TakeOnSuccess)
2025+
srcType->vw_destroy(src);
2026+
2027+
return embedded;
2028+
}
2029+
20132030
BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src,
20142031
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
20152032
return swift_bridgeErrorToNSError((SwiftError*)errorBox.first);
20162033
}
2034+
20172035
#endif
20182036

20192037
namespace {

test/1_stdlib/ErrorBridged.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,4 +590,41 @@ ErrorBridgingTests.test("NSError subclass identity") {
590590
expectTrue(type(of: nsError) == MyNSError.self)
591591
}
592592

593+
ErrorBridgingTests.test("Wrapped NSError identity") {
594+
let nsError = NSError(domain: NSCocoaErrorDomain,
595+
code: NSFileNoSuchFileError,
596+
userInfo: [
597+
AnyHashable(NSFilePathErrorKey) : "/dev/null",
598+
AnyHashable(NSStringEncodingErrorKey): /*ASCII=*/1,
599+
])
600+
601+
let error: Error = nsError
602+
let nsError2: NSError = error as NSError
603+
expectTrue(nsError === nsError2)
604+
605+
// Extracting the NSError via the runtime.
606+
let cocoaErrorAny: Any = error as! CocoaError
607+
let nsError3: NSError = cocoaErrorAny as! NSError
608+
expectTrue(nsError === nsError3)
609+
610+
if let cocoaErrorAny2: Any = error as? CocoaError {
611+
let nsError4: NSError = cocoaErrorAny2 as! NSError
612+
expectTrue(nsError === nsError4)
613+
} else {
614+
expectUnreachable()
615+
}
616+
617+
// Extracting the NSError via direct call.
618+
let cocoaError = error as! CocoaError
619+
let nsError5: NSError = cocoaError as NSError
620+
expectTrue(nsError === nsError5)
621+
622+
if let cocoaError2 = error as? CocoaError {
623+
let nsError6: NSError = cocoaError as NSError
624+
expectTrue(nsError === nsError6)
625+
} else {
626+
expectUnreachable()
627+
}
628+
}
629+
593630
runAllTests()

test/SILGen/objc_error.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,13 @@ class MyNSError : NSError {
108108
func eraseMyNSError() -> Error {
109109
return MyNSError()
110110
}
111+
112+
// CHECK-LABEL: sil hidden @_TF10objc_error25eraseFictionalServerErrorFT_Ps5Error_
113+
func eraseFictionalServerError() -> Error {
114+
// CHECK-NOT: return
115+
// CHECK: [[NSERROR_GETTER:%[0-9]+]] = function_ref @_TFVSC20FictionalServerErrorg8_nsErrorCSo7NSError
116+
// CHECK: [[NSERROR:%[0-9]+]] = apply [[NSERROR_GETTER]]
117+
// CHECK: [[ERROR:%[0-9]+]] = init_existential_ref [[NSERROR]]
118+
// CHECK: return [[ERROR]]
119+
return FictionalServerError(.meltedDown)
120+
}

0 commit comments

Comments
 (0)