@@ -450,23 +450,33 @@ ManagedValue SILGenFunction::emitExistentialErasure(
450
450
const TypeLowering &existentialTL,
451
451
ArrayRef<ProtocolConformanceRef> conformances,
452
452
SGFContext C,
453
- llvm::function_ref<ManagedValue (SGFContext)> F) {
453
+ llvm::function_ref<ManagedValue (SGFContext)> F,
454
+ bool allowEmbeddedNSError) {
454
455
// Mark the needed conformances as used.
455
456
for (auto conformance : conformances)
456
457
SGM.useConformance (conformance);
457
458
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.
461
461
auto &ctx = getASTContext ();
462
- if (existentialTL.getSemanticType ().getSwiftRValueType ()->getAnyNominal () ==
462
+ auto nsError = ctx.getNSErrorDecl ();
463
+ if (allowEmbeddedNSError && nsError &&
464
+ existentialTL.getSemanticType ().getSwiftRValueType ()->getAnyNominal () ==
463
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.
464
469
auto conformance =
465
470
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
+
467
479
if (conformance && nsError && SGM.getNSErrorConformanceToError ()) {
468
- CanType nsErrorType =
469
- nsError->getDeclaredInterfaceType ()->getCanonicalType ();
470
480
if (auto witness =
471
481
conformance->getWitness (SGM.getNSErrorRequirement (loc), nullptr )) {
472
482
// Create a reference to the getter witness.
@@ -480,10 +490,6 @@ ManagedValue SILGenFunction::emitExistentialErasure(
480
490
SGM.SwiftModule , nullptr );
481
491
482
492
// Emit the erasure, through the getter to _nsError.
483
- ProtocolConformanceRef nsErrorConformances[1 ] = {
484
- ProtocolConformanceRef (SGM.getNSErrorConformanceToError ())
485
- };
486
-
487
493
return emitExistentialErasure (
488
494
loc, nsErrorType,
489
495
getTypeLowering (nsErrorType),
@@ -504,6 +510,97 @@ ManagedValue SILGenFunction::emitExistentialErasure(
504
510
});
505
511
}
506
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.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
+ }
507
604
}
508
605
509
606
switch (existentialTL.getLoweredType ().getObjectType ()
0 commit comments