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