Skip to content

Commit 967a1d4

Browse files
authored
Merge pull request #3976 from DougGregor/swift-3-nserror-wrapping
[Swift 3.0] Eliminate double-wrapping of NSErrors in _SwiftNativeNSError
2 parents 7d2ee67 + 171a18c commit 967a1d4

File tree

13 files changed

+450
-9
lines changed

13 files changed

+450
-9
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(GetErrorEmbeddedNSError, "_stdlib_getErrorEmbeddedNSError")
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/SIL/SILType.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,8 @@ static bool isBridgedErrorClass(SILModule &M,
465465
t = archetypeType->getSuperclass();
466466

467467
// NSError (TODO: and CFError) can be bridged.
468-
auto errorType = M.Types.getNSErrorType();
469-
if (t && errorType && t->isEqual(errorType)) {
468+
auto nsErrorType = M.Types.getNSErrorType();
469+
if (t && nsErrorType && nsErrorType->isExactSuperclassOf(t, nullptr)) {
470470
return true;
471471
}
472472

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();
@@ -377,6 +382,20 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
377382
ProtocolConformance *getConformanceToObjectiveCBridgeable(SILLocation loc,
378383
Type type);
379384

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

lib/SILGen/SILGenConvert.cpp

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,159 @@ 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

459+
// If we're erasing to the 'Error' type, we might be able to get an NSError
460+
// representation more efficiently.
461+
auto &ctx = getASTContext();
462+
auto nsError = ctx.getNSErrorDecl();
463+
if (allowEmbeddedNSError && nsError &&
464+
existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() ==
465+
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.
469+
auto conformance =
470+
SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType);
471+
472+
CanType nsErrorType =
473+
nsError->getDeclaredInterfaceType()->getCanonicalType();
474+
475+
ProtocolConformanceRef nsErrorConformances[1] = {
476+
ProtocolConformanceRef(SGM.getNSErrorConformanceToError())
477+
};
478+
479+
if (conformance && nsError && SGM.getNSErrorConformanceToError()) {
480+
if (auto witness =
481+
conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) {
482+
// Create a reference to the getter witness.
483+
SILDeclRef getter =
484+
getGetterDeclRef(cast<VarDecl>(witness.getDecl()),
485+
/*isDirectAccessorUse=*/true);
486+
487+
// Compute the substitutions.
488+
ArrayRef<Substitution> substitutions =
489+
concreteFormalType->gatherAllSubstitutions(
490+
SGM.SwiftModule, nullptr);
491+
492+
// Emit the erasure, through the getter to _nsError.
493+
return emitExistentialErasure(
494+
loc, nsErrorType,
495+
getTypeLowering(nsErrorType),
496+
existentialTL,
497+
ctx.AllocateCopy(nsErrorConformances),
498+
C,
499+
[&](SGFContext innerC) -> ManagedValue {
500+
// Call the getter.
501+
return emitGetAccessor(loc, getter, substitutions,
502+
ArgumentSource(loc,
503+
RValue(*this, loc,
504+
concreteFormalType,
505+
F(SGFContext()))),
506+
/*isSuper=*/false,
507+
/*isDirectAccessorUse=*/true,
508+
RValue(), innerC)
509+
.getAsSingleValue(*this, loc);
510+
});
511+
}
512+
}
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.getGetErrorEmbeddedNSError(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+
}
604+
}
605+
458606
switch (existentialTL.getLoweredType().getObjectType()
459607
.getPreferredExistentialRepresentation(SGM.M, concreteFormalType)) {
460608
case ExistentialRepresentation::None:

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
944944
const TypeLowering &existentialTL,
945945
ArrayRef<ProtocolConformanceRef> conformances,
946946
SGFContext C,
947-
llvm::function_ref<ManagedValue (SGFContext)> F);
947+
llvm::function_ref<ManagedValue (SGFContext)> F,
948+
bool allowEmbeddedNSError = true);
948949

949950
//===--------------------------------------------------------------------===//
950951
// Recursive entry points

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 15 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
@@ -496,6 +506,11 @@ extension _ErrorCodeProtocol where Self._ErrorType: _BridgedStoredNSError {
496506
}
497507

498508
extension _BridgedStoredNSError {
509+
/// Retrieve the embedded NSError from a bridged, stored NSError.
510+
public func _getEmbeddedNSError() -> AnyObject? {
511+
return _nsError
512+
}
513+
499514
public static func == (lhs: Self, rhs: Self) -> Bool {
500515
return lhs._nsError.isEqual(rhs._nsError)
501516
}

0 commit comments

Comments
 (0)