Skip to content

Commit 75e85dc

Browse files
committed
[NSError bridging] Extract embedded NSError from an Error consistently.
When emitting an existential erasure to Error from an archetype, use the _getEmbeddedNSError() witness. If it produces an NSError, erase that; otherwise, go through the normal erasure path. Of course, make NSError and CFError implement _getEmbeddedNSError() so this kicks in for the obvious cases as well as the more obscure ones. Fixes the rest of SR-1562 / rdar://problem/26370984.
1 parent 02d2517 commit 75e85dc

File tree

9 files changed

+172
-15
lines changed

9 files changed

+172
-15
lines changed

include/swift/AST/ASTContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ class ASTContext {
364364
PrecedenceGroupDecl *right) const;
365365

366366
/// Retrieve the declaration of Swift.Error.
367-
NominalTypeDecl *getErrorDecl() const;
367+
ProtocolDecl *getErrorDecl() const;
368368
CanType getExceptionType() const;
369369

370370
/// Retrieve the declaration of Swift.Bool.

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,6 @@ FUNC_DECL(BridgeAnythingToObjectiveC,
7171
FUNC_DECL(DidEnterMain, "_stdlib_didEnterMain")
7272
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
7373

74+
FUNC_DECL(GetErrorEmbeddedNSErrorValue, "_stdlib_getErrorEmbeddedNSErrorValue")
75+
7476
#undef FUNC_DECL

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ CanType ASTContext::getExceptionType() const {
612612
}
613613
}
614614

615-
NominalTypeDecl *ASTContext::getErrorDecl() const {
615+
ProtocolDecl *ASTContext::getErrorDecl() const {
616616
return getProtocol(KnownProtocolKind::Error);
617617
}
618618

lib/SILGen/SILGenConvert.cpp

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -450,23 +450,33 @@ ManagedValue SILGenFunction::emitExistentialErasure(
450450
const TypeLowering &existentialTL,
451451
ArrayRef<ProtocolConformanceRef> conformances,
452452
SGFContext C,
453-
llvm::function_ref<ManagedValue (SGFContext)> F) {
453+
llvm::function_ref<ManagedValue (SGFContext)> F,
454+
bool allowEmbeddedNSError) {
454455
// Mark the needed conformances as used.
455456
for (auto conformance : conformances)
456457
SGM.useConformance(conformance);
457458

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.
459+
// If we're erasing to the 'Error' type, we might be able to get an NSError
460+
// representation more efficiently.
461461
auto &ctx = getASTContext();
462-
if (existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() ==
462+
auto nsError = ctx.getNSErrorDecl();
463+
if (allowEmbeddedNSError && nsError &&
464+
existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() ==
463465
ctx.getErrorDecl()) {
466+
// Check whether the concrete type conforms to the _BridgedStoredNSError
467+
// protocol. In that case, call the _nsError witness getter to extract the
468+
// NSError directly.
464469
auto conformance =
465470
SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType);
466-
auto nsError = ctx.getNSErrorDecl();
471+
472+
CanType nsErrorType =
473+
nsError->getDeclaredInterfaceType()->getCanonicalType();
474+
475+
ProtocolConformanceRef nsErrorConformances[1] = {
476+
ProtocolConformanceRef(SGM.getNSErrorConformanceToError())
477+
};
478+
467479
if (conformance && nsError && SGM.getNSErrorConformanceToError()) {
468-
CanType nsErrorType =
469-
nsError->getDeclaredInterfaceType()->getCanonicalType();
470480
if (auto witness =
471481
conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) {
472482
// Create a reference to the getter witness.
@@ -480,10 +490,6 @@ ManagedValue SILGenFunction::emitExistentialErasure(
480490
SGM.SwiftModule, nullptr);
481491

482492
// Emit the erasure, through the getter to _nsError.
483-
ProtocolConformanceRef nsErrorConformances[1] = {
484-
ProtocolConformanceRef(SGM.getNSErrorConformanceToError())
485-
};
486-
487493
return emitExistentialErasure(
488494
loc, nsErrorType,
489495
getTypeLowering(nsErrorType),
@@ -504,6 +510,97 @@ ManagedValue SILGenFunction::emitExistentialErasure(
504510
});
505511
}
506512
}
513+
514+
// Check whether the concrete type is an archetype. If so, call the
515+
// _getEmbeddedNSError() witness to try to dig out the embedded NSError.
516+
if (auto archetypeType = concreteFormalType->getAs<ArchetypeType>()) {
517+
if (std::find(archetypeType->getConformsTo().begin(),
518+
archetypeType->getConformsTo().end(),
519+
ctx.getErrorDecl())
520+
!= archetypeType->getConformsTo().end()) {
521+
auto contBB = createBasicBlock();
522+
auto isNotPresentBB = createBasicBlock();
523+
auto isPresentBB = createBasicBlock();
524+
525+
SILValue existentialResult =
526+
contBB->createBBArg(existentialTL.getLoweredType());
527+
528+
ProtocolConformanceRef trivialErrorConformances[1] = {
529+
ProtocolConformanceRef(ctx.getErrorDecl())
530+
};
531+
532+
Substitution substitutions[1] = {
533+
Substitution(concreteFormalType,
534+
ctx.AllocateCopy(trivialErrorConformances))
535+
};
536+
537+
// Call swift_stdlib_getErrorEmbeddedNSError to attempt to extract an
538+
// NSError from the value.
539+
ManagedValue concreteValue = F(SGFContext());
540+
ManagedValue potentialNSError =
541+
emitApplyOfLibraryIntrinsic(loc,
542+
SGM.getGetErrorEmbeddedNSErrorValue(loc),
543+
ctx.AllocateCopy(substitutions),
544+
{ concreteValue },
545+
SGFContext())
546+
.getAsSingleValue(*this, loc);
547+
548+
// Check whether we got an NSError back.
549+
SILValue hasNSError =
550+
emitDoesOptionalHaveValue(loc, potentialNSError.getValue());
551+
552+
B.createCondBranch(loc, hasNSError, isPresentBB, isNotPresentBB);
553+
554+
// If we did get an NSError, emit the existential erasure from that
555+
// NSError.
556+
B.emitBlock(isPresentBB);
557+
SILValue branchArg;
558+
{
559+
// Don't allow cleanups to escape the conditional block.
560+
FullExpr presentScope(Cleanups, CleanupLocation::get(loc));
561+
562+
// Emit the existential erasure from the NSError.
563+
branchArg = emitExistentialErasure(
564+
loc, nsErrorType,
565+
getTypeLowering(nsErrorType),
566+
existentialTL,
567+
ctx.AllocateCopy(nsErrorConformances),
568+
C,
569+
[&](SGFContext innerC) -> ManagedValue {
570+
// Pull the NSError object out of the optional result.
571+
auto &inputTL = getTypeLowering(potentialNSError.getType());
572+
auto nsErrorValue =
573+
emitUncheckedGetOptionalValueFrom(loc, potentialNSError,
574+
inputTL);
575+
576+
577+
// Perform an unchecked cast down to NSError, because it was typed
578+
// as 'AnyObject' for layering reasons.
579+
return ManagedValue(B.createUncheckedRefCast(
580+
loc,
581+
nsErrorValue.getValue(),
582+
getLoweredType(nsErrorType)),
583+
nsErrorValue.getCleanup());
584+
585+
}).forward(*this);
586+
}
587+
B.createBranch(loc, contBB, branchArg);
588+
589+
// If we did not get an NSError, just directly emit the existential
590+
// (recursively).
591+
B.emitBlock(isNotPresentBB);
592+
branchArg = emitExistentialErasure(loc, concreteFormalType, concreteTL,
593+
existentialTL, conformances,
594+
SGFContext(), F,
595+
/*allowEmbeddedNSError=*/false)
596+
.forward(*this);
597+
B.createBranch(loc, contBB, branchArg);
598+
599+
// Continue.
600+
B.emitBlock(contBB);
601+
return emitManagedRValueWithCleanup(existentialResult, existentialTL);
602+
}
603+
}
507604
}
508605

509606
switch (existentialTL.getLoweredType().getObjectType()

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
946946
const TypeLowering &existentialTL,
947947
ArrayRef<ProtocolConformanceRef> conformances,
948948
SGFContext C,
949-
llvm::function_ref<ManagedValue (SGFContext)> F);
949+
llvm::function_ref<ManagedValue (SGFContext)> F,
950+
bool allowEmbeddedNSError = true);
950951

951952
//===--------------------------------------------------------------------===//
952953
// Recursive entry points

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ extension NSError : Error {
241241
public var _domain: String { return domain }
242242
public var _code: Int { return code }
243243
public var _userInfo: Any? { return userInfo }
244+
245+
/// The "embedded" NSError is itself.
246+
public func _getEmbeddedNSError() -> AnyObject? {
247+
return self
248+
}
244249
}
245250

246251
extension CFError : Error {
@@ -255,6 +260,11 @@ extension CFError : Error {
255260
public var _userInfo: Any? {
256261
return CFErrorCopyUserInfo(self) as Any
257262
}
263+
264+
/// The "embedded" NSError is itself.
265+
public func _getEmbeddedNSError() -> AnyObject? {
266+
return self
267+
}
258268
}
259269

260270
// An error value to use when an Objective-C API indicates error

stdlib/public/core/ErrorType.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ public func _stdlib_getErrorEmbeddedNSError<T : Error>(_ x: UnsafePointer<T>)
155155
return x.pointee._getEmbeddedNSError()
156156
}
157157

158+
/// FIXME: Quite unfortunate to have both of these.
159+
@_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorValue")
160+
public func _stdlib_getErrorEmbeddedNSErrorValue<T : Error>(_ x: T)
161+
-> AnyObject? {
162+
return x._getEmbeddedNSError()
163+
}
164+
158165
@_silgen_name("swift_stdlib_getErrorDefaultUserInfo")
159166
public func _stdlib_getErrorDefaultUserInfo(_ error: Error) -> AnyObject?
160167

test/1_stdlib/ErrorBridged.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,4 +627,17 @@ ErrorBridgingTests.test("Wrapped NSError identity") {
627627
}
628628
}
629629

630+
extension Error {
631+
func asNSError() -> NSError {
632+
return self as NSError
633+
}
634+
}
635+
636+
// SR-1562
637+
ErrorBridgingTests.test("Error archetype identity") {
638+
let myError = NSError(domain: "myErrorDomain", code: 0,
639+
userInfo: [ AnyHashable("one") : 1 ])
640+
expectTrue(myError === myError.asNSError())
641+
}
642+
630643
runAllTests()

test/SILGen/objc_error.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,30 @@ func eraseFictionalServerError() -> Error {
118118
// CHECK: return [[ERROR]]
119119
return FictionalServerError(.meltedDown)
120120
}
121+
122+
// SR-1562
123+
extension Error {
124+
// CHECK-LABEL: sil hidden @_TFE10objc_errorPs5Error16convertToNSErrorfT_CSo7NSError
125+
// CHECK: bb0([[SELF:%[0-9]+]] : $*Self)
126+
func convertToNSError() -> NSError {
127+
// CHECK: [[GET_EMBEDDED_FN:%[0-9]+]] = function_ref @swift_stdlib_getErrorEmbeddedNSErrorValue
128+
// CHECK: [[EMBEDDED_RESULT_OPT:%[0-9]+]] = apply [[GET_EMBEDDED_FN]]
129+
// CHECK: [[HAS_EMBEDDED_RESULT:%[0-9]+]] = select_enum [[EMBEDDED_RESULT_OPT]] : $Optional<AnyObject>
130+
// CHECK: cond_br [[HAS_EMBEDDED_RESULT]], [[SUCCESS:bb[0-9]+]], [[FAILURE:bb[0-9]+]]
131+
132+
// CHECK: [[SUCCESS]]:
133+
// CHECK: [[EMBEDDED_RESULT:%[0-9]+]] = unchecked_enum_data [[EMBEDDED_RESULT_OPT]] : $Optional<AnyObject>, #Optional.some!enumelt.1
134+
// CHECK: [[EMBEDDED_NSERROR:%[0-9]+]] = unchecked_ref_cast [[EMBEDDED_RESULT]] : $AnyObject to $NSError
135+
// CHECK: [[ERROR:%[0-9]+]] = init_existential_ref [[EMBEDDED_NSERROR]] : $NSError : $NSError, $Error
136+
// CHECK: br [[CONTINUATION:bb[0-9]+]]([[ERROR]] : $Error)
137+
138+
// CHECK: [[FAILURE]]:
139+
// CHECK: [[ERROR_BOX:%[0-9]+]] = alloc_existential_box $Error, $Self
140+
// CHECK: [[ERROR_PROJECTED:%[0-9]+]] = project_existential_box $Self in [[ERROR_BOX]] : $Error
141+
// CHECK: copy_addr [[SELF]] to [initialization] [[ERROR_PROJECTED]] : $*Self
142+
// CHECK: br [[CONTINUATION]]([[ERROR_BOX]] : $Error)
143+
144+
// CHECK: [[CONTINUATION]]([[ERROR_ARG:%[0-9]+]] : $Error):
145+
return self as NSError
146+
}
147+
}

0 commit comments

Comments
 (0)