@@ -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 ()
0 commit comments