Skip to content

Commit 7fb3db2

Browse files
authored
Merge pull request #4279 from rjmccall/error-erasure
Don't double-emit the source expression when erasing an archetype to Error
2 parents 367ac4a + b974fbe commit 7fb3db2

File tree

5 files changed

+174
-170
lines changed

5 files changed

+174
-170
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,15 +319,13 @@ VarDecl *SILGenModule::getNSErrorRequirement(SILLocation loc) {
319319
return found;
320320
}
321321

322-
ProtocolConformance *
322+
Optional<ProtocolConformanceRef>
323323
SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) {
324324
auto proto = getBridgedStoredNSError(loc);
325-
if (!proto) return nullptr;
325+
if (!proto) return None;
326326

327327
// Find the conformance to _BridgedStoredNSError.
328-
auto result = SwiftModule->lookupConformance(type, proto, nullptr);
329-
if (result) return result->getConcrete();
330-
return nullptr;
328+
return SwiftModule->lookupConformance(type, proto, nullptr);
331329
}
332330

333331
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {

lib/SILGen/SILGen.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
393393

394394
/// Find the conformance of the given Swift type to the
395395
/// _BridgedStoredNSError protocol.
396-
ProtocolConformance *getConformanceToBridgedStoredNSError(SILLocation loc,
397-
Type type);
396+
Optional<ProtocolConformanceRef>
397+
getConformanceToBridgedStoredNSError(SILLocation loc, Type type);
398398

399399
/// Retrieve the conformance of NSError to the Error protocol.
400400
ProtocolConformance *getNSErrorConformanceToError();

lib/SILGen/SILGenBridging.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -738,20 +738,29 @@ ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc,
738738
llvm_unreachable("bad CC");
739739
}
740740

741-
/// Bridge an optional foreign error type to Error.
741+
/// Bridge a possibly-optional foreign error type to Error.
742742
ManagedValue SILGenFunction::emitBridgedToNativeError(SILLocation loc,
743743
ManagedValue bridgedError) {
744-
// If the incoming error is non-optional, inject it into an optional.
745-
Type bridgedErrorTy = bridgedError.getType().getSwiftRValueType();
746-
if (!bridgedErrorTy->getAnyOptionalObjectType()) {
747-
SILType loweredOptErrorTy =
748-
SGM.getLoweredType(OptionalType::get(bridgedErrorTy));
749-
auto *someDecl = getASTContext().getOptionalSomeDecl();
750-
auto *enumInst = B.createEnum(loc, bridgedError.getValue(), someDecl,
751-
loweredOptErrorTy);
752-
bridgedError = ManagedValue(enumInst, bridgedError.getCleanup());
744+
// If the incoming error is non-optional, just do an existential erasure.
745+
CanType bridgedErrorTy = bridgedError.getType().getSwiftRValueType();
746+
if (!bridgedErrorTy.getAnyOptionalObjectType()) {
747+
auto nativeErrorTy = SILType::getExceptionType(getASTContext());
748+
749+
auto conformance = SGM.getNSErrorConformanceToError();
750+
if (!conformance) return emitUndef(loc, nativeErrorTy);
751+
ProtocolConformanceRef conformanceArray[] = {
752+
ProtocolConformanceRef(conformance)
753+
};
754+
auto conformances = getASTContext().AllocateCopy(conformanceArray);
755+
756+
SILValue nativeError =
757+
B.createInitExistentialRef(loc, nativeErrorTy, bridgedErrorTy,
758+
bridgedError.forward(*this), conformances);
759+
return emitManagedRValueWithCleanup(nativeError);
753760
}
754761

762+
// Otherwise, we need to call a runtime function to potential substitute
763+
// a standard error for a nil NSError.
755764
auto bridgeFn = emitGlobalFunctionRef(loc, SGM.getNSErrorToErrorFn());
756765
auto bridgeFnType = bridgeFn->getType().castTo<SILFunctionType>();
757766
assert(bridgeFnType->getNumAllResults() == 1);

lib/SILGen/SILGenConvert.cpp

Lines changed: 134 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -459,147 +459,145 @@ ManagedValue SILGenFunction::emitExistentialErasure(
459459
// If we're erasing to the 'Error' type, we might be able to get an NSError
460460
// representation more efficiently.
461461
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-
});
462+
if (conformances.size() == 1 &&
463+
conformances[0].getRequirement() == ctx.getErrorDecl() &&
464+
ctx.getNSErrorDecl()) {
465+
auto nsErrorDecl = ctx.getNSErrorDecl();
466+
467+
// If the concrete type is NSError or a subclass thereof, just erase it
468+
// directly.
469+
auto nsErrorType = nsErrorDecl->getDeclaredType()->getCanonicalType();
470+
if (nsErrorType->isExactSuperclassOf(concreteFormalType, nullptr)) {
471+
ManagedValue nsError = F(SGFContext());
472+
if (nsErrorType != concreteFormalType) {
473+
nsError = ManagedValue(B.createUpcast(loc, nsError.getValue(),
474+
getLoweredType(nsErrorType)),
475+
nsError.getCleanup());
511476
}
477+
return emitBridgedToNativeError(loc, nsError);
512478
}
513479

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);
480+
// If the concrete type is known to conform to _BridgedStoredNSError,
481+
// call the _nsError witness getter to extract the NSError directly,
482+
// then just erase the NSError.
483+
if (auto storedNSErrorConformance =
484+
SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType)) {
485+
auto nsErrorVar = SGM.getNSErrorRequirement(loc);
486+
if (!nsErrorVar) return emitUndef(loc, existentialTL.getLoweredType());
487+
488+
ArrayRef<Substitution> nsErrorVarSubstitutions;
489+
490+
// Devirtualize. Maybe this should be done implicitly by
491+
// emitPropertyLValue?
492+
if (storedNSErrorConformance->isConcrete()) {
493+
if (auto witnessVar = storedNSErrorConformance->getConcrete()
494+
->getWitness(nsErrorVar, nullptr)) {
495+
nsErrorVar = cast<VarDecl>(witnessVar.getDecl());
496+
nsErrorVarSubstitutions = witnessVar.getSubstitutions();
586497
}
587-
B.createBranch(loc, contBB, branchArg);
498+
}
588499

589-
// If we did not get an NSError, just directly emit the existential
590-
// (recursively).
591-
B.emitBlock(isNotPresentBB);
500+
auto nativeError = F(SGFContext());
501+
502+
WritebackScope writebackScope(*this);
503+
auto nsError =
504+
emitRValueForPropertyLoad(loc, nativeError, concreteFormalType,
505+
/*super*/ false, nsErrorVar,
506+
nsErrorVarSubstitutions,
507+
AccessSemantics::Ordinary, nsErrorType,
508+
SGFContext())
509+
.getAsSingleValue(*this, loc);
510+
511+
return emitBridgedToNativeError(loc, nsError);
512+
}
513+
514+
// Otherwise, if it's an archetype, try calling the _getEmbeddedNSError()
515+
// witness to try to dig out the embedded NSError. But don't do this
516+
// when we're being called recursively.
517+
if (isa<ArchetypeType>(concreteFormalType) && allowEmbeddedNSError) {
518+
auto contBB = createBasicBlock();
519+
auto isNotPresentBB = createBasicBlock();
520+
auto isPresentBB = createBasicBlock();
521+
522+
// Call swift_stdlib_getErrorEmbeddedNSError to attempt to extract an
523+
// NSError from the value.
524+
auto getEmbeddedNSErrorFn = SGM.getGetErrorEmbeddedNSError(loc);
525+
if (!getEmbeddedNSErrorFn)
526+
return emitUndef(loc, existentialTL.getLoweredType());
527+
528+
Substitution getEmbeddedNSErrorSubstitutions[1] = {
529+
Substitution(concreteFormalType, conformances)
530+
};
531+
532+
ManagedValue concreteValue = F(SGFContext());
533+
ManagedValue potentialNSError =
534+
emitApplyOfLibraryIntrinsic(loc,
535+
getEmbeddedNSErrorFn,
536+
getEmbeddedNSErrorSubstitutions,
537+
{ concreteValue.copy(*this, loc) },
538+
SGFContext())
539+
.getAsSingleValue(*this, loc);
540+
541+
// We're going to consume 'concreteValue' in exactly one branch,
542+
// so kill its cleanup now and recreate it on both branches.
543+
(void) concreteValue.forward(*this);
544+
545+
// Check whether we got an NSError back.
546+
std::pair<EnumElementDecl*, SILBasicBlock*> cases[] = {
547+
{ ctx.getOptionalSomeDecl(), isPresentBB },
548+
{ ctx.getOptionalNoneDecl(), isNotPresentBB }
549+
};
550+
B.createSwitchEnum(loc, potentialNSError.forward(*this),
551+
/*default*/ nullptr, cases);
552+
553+
// If we did get an NSError, emit the existential erasure from that
554+
// NSError.
555+
B.emitBlock(isPresentBB);
556+
SILValue branchArg;
557+
{
558+
// Don't allow cleanups to escape the conditional block.
559+
FullExpr presentScope(Cleanups, CleanupLocation::get(loc));
560+
enterDestroyCleanup(concreteValue.getValue());
561+
562+
// Receive the error value. It's typed as an 'AnyObject' for
563+
// layering reasons, so perform an unchecked cast down to NSError.
564+
OptionalTypeKind optKind;
565+
SILType anyObjectTy =
566+
potentialNSError.getType().getAnyOptionalObjectType(SGM.M, optKind);
567+
SILValue nsError = isPresentBB->createBBArg(anyObjectTy);
568+
nsError = B.createUncheckedRefCast(loc, nsError,
569+
getLoweredType(nsErrorType));
570+
571+
branchArg = emitBridgedToNativeError(loc,
572+
emitManagedRValueWithCleanup(nsError))
573+
.forward(*this);
574+
}
575+
B.createBranch(loc, contBB, branchArg);
576+
577+
// If we did not get an NSError, just directly emit the existential.
578+
// Since this is a recursive call, make sure we don't end up in this
579+
// path again.
580+
B.emitBlock(isNotPresentBB);
581+
{
582+
FullExpr presentScope(Cleanups, CleanupLocation::get(loc));
583+
concreteValue = emitManagedRValueWithCleanup(concreteValue.getValue());
592584
branchArg = emitExistentialErasure(loc, concreteFormalType, concreteTL,
593585
existentialTL, conformances,
594-
SGFContext(), F,
586+
SGFContext(),
587+
[&](SGFContext C) {
588+
return concreteValue;
589+
},
595590
/*allowEmbeddedNSError=*/false)
596591
.forward(*this);
597-
B.createBranch(loc, contBB, branchArg);
598-
599-
// Continue.
600-
B.emitBlock(contBB);
601-
return emitManagedRValueWithCleanup(existentialResult, existentialTL);
602592
}
593+
B.createBranch(loc, contBB, branchArg);
594+
595+
// Continue.
596+
B.emitBlock(contBB);
597+
598+
SILValue existentialResult =
599+
contBB->createBBArg(existentialTL.getLoweredType());
600+
return emitManagedRValueWithCleanup(existentialResult, existentialTL);
603601
}
604602
}
605603

@@ -641,14 +639,12 @@ ManagedValue SILGenFunction::emitExistentialErasure(
641639
concreteTL.getLoweredType(),
642640
existential);
643641
// Initialize the concrete value in-place.
644-
InitializationPtr init(
645-
new ExistentialInitialization(existential, valueAddr, concreteFormalType,
646-
ExistentialRepresentation::Boxed,
647-
*this));
648-
ManagedValue mv = F(SGFContext(init.get()));
642+
ExistentialInitialization init(existential, valueAddr, concreteFormalType,
643+
ExistentialRepresentation::Boxed, *this);
644+
ManagedValue mv = F(SGFContext(&init));
649645
if (!mv.isInContext()) {
650-
mv.forwardInto(*this, loc, init->getAddress());
651-
init->finishInitialization(*this);
646+
mv.forwardInto(*this, loc, init.getAddress());
647+
init.finishInitialization(*this);
652648
}
653649

654650
return emitManagedRValueWithCleanup(existential);

0 commit comments

Comments
 (0)