@@ -3381,6 +3381,7 @@ getArchetypeAndRootOpaqueArchetype(Type maybeOpaqueType) {
3381
3381
OpaqueSubstitutionKind
3382
3382
ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution (
3383
3383
OpaqueTypeDecl *opaque) const {
3384
+ const auto *inContext = getContext ();
3384
3385
auto inModule = inContext ? inContext->getParentModule ()
3385
3386
: opaque->getParentModule ();
3386
3387
return shouldPerformSubstitution (opaque, inModule, contextExpansion);
@@ -3424,12 +3425,11 @@ ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
3424
3425
return OpaqueSubstitutionKind::SubstituteNonResilientModule;
3425
3426
}
3426
3427
3427
- static Type
3428
- substOpaqueTypesWithUnderlyingTypes (Type ty, const DeclContext *inContext,
3429
- ResilienceExpansion contextExpansion,
3430
- bool isWholeModuleContext) {
3428
+ static Type substOpaqueTypesWithUnderlyingTypesRec (
3429
+ Type ty, const DeclContext *inContext, ResilienceExpansion contextExpansion,
3430
+ bool isWholeModuleContext, SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3431
3431
ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
3432
- isWholeModuleContext);
3432
+ isWholeModuleContext, decls );
3433
3433
return ty.subst (replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
3434
3434
}
3435
3435
@@ -3489,6 +3489,13 @@ static bool canSubstituteTypeInto(Type ty, const DeclContext *dc,
3489
3489
llvm_unreachable (" invalid subsitution kind" );
3490
3490
}
3491
3491
3492
+ ReplaceOpaqueTypesWithUnderlyingTypes::ReplaceOpaqueTypesWithUnderlyingTypes (
3493
+ const DeclContext *inContext, ResilienceExpansion contextExpansion,
3494
+ bool isWholeModuleContext, llvm::SmallPtrSetImpl<OpaqueTypeDecl *> &seen)
3495
+ : contextExpansion(contextExpansion),
3496
+ inContextAndIsWholeModule(inContext, isWholeModuleContext),
3497
+ seenDecls(&seen) {}
3498
+
3492
3499
Type ReplaceOpaqueTypesWithUnderlyingTypes::
3493
3500
operator ()(SubstitutableType *maybeOpaqueType) const {
3494
3501
auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype (maybeOpaqueType);
@@ -3516,8 +3523,8 @@ operator()(SubstitutableType *maybeOpaqueType) const {
3516
3523
3517
3524
// Check that we are allowed to substitute the underlying type into the
3518
3525
// context.
3519
- auto inContext = this ->inContext ;
3520
- auto isContextWholeModule = this ->isContextWholeModule ;
3526
+ auto inContext = this ->getContext () ;
3527
+ auto isContextWholeModule = this ->isWholeModule () ;
3521
3528
if (inContext &&
3522
3529
partialSubstTy.findIf (
3523
3530
[inContext, substitutionKind, isContextWholeModule](Type t) -> bool {
@@ -3536,18 +3543,39 @@ operator()(SubstitutableType *maybeOpaqueType) const {
3536
3543
3537
3544
// If the type changed, but still contains opaque types, recur.
3538
3545
if (!substTy->isEqual (maybeOpaqueType) && substTy->hasOpaqueArchetype ()) {
3539
- return ::substOpaqueTypesWithUnderlyingTypes (
3540
- substTy, inContext, contextExpansion, isContextWholeModule);
3546
+ if (auto *alreadySeen = this ->seenDecls ) {
3547
+ // Detect substitution loops. If we find one, just bounce the original
3548
+ // type back to the caller. This substitution will fail at runtime
3549
+ // instead.
3550
+ if (!alreadySeen->insert (opaqueRoot->getDecl ()).second ) {
3551
+ return maybeOpaqueType;
3552
+ }
3553
+
3554
+ auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
3555
+ substTy, inContext, contextExpansion, isContextWholeModule,
3556
+ *alreadySeen);
3557
+ alreadySeen->erase (opaqueRoot->getDecl ());
3558
+ return res;
3559
+ } else {
3560
+ // We're the top of the stack for the recursion check. Allocate a set of
3561
+ // opaque result type decls we've already seen for the rest of the check.
3562
+ SmallPtrSet<OpaqueTypeDecl *, 8 > seenDecls;
3563
+ seenDecls.insert (opaqueRoot->getDecl ());
3564
+ return ::substOpaqueTypesWithUnderlyingTypesRec (
3565
+ substTy, inContext, contextExpansion, isContextWholeModule,
3566
+ seenDecls);
3567
+ }
3541
3568
}
3542
3569
3543
3570
return substTy;
3544
3571
}
3545
3572
3546
- static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes (
3573
+ static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec (
3547
3574
ProtocolConformanceRef ref, Type origType, const DeclContext *inContext,
3548
- ResilienceExpansion contextExpansion, bool isWholeModuleContext) {
3575
+ ResilienceExpansion contextExpansion, bool isWholeModuleContext,
3576
+ SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3549
3577
ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
3550
- isWholeModuleContext);
3578
+ isWholeModuleContext, decls );
3551
3579
return ref.subst (origType, replacer, replacer,
3552
3580
SubstFlags::SubstituteOpaqueArchetypes);
3553
3581
}
@@ -3575,6 +3603,7 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3575
3603
// SIL type lowering may have already substituted away the opaque type, in
3576
3604
// which case we'll end up "substituting" the same type.
3577
3605
if (maybeOpaqueType->isEqual (replacementType)) {
3606
+ const auto *inContext = getContext ();
3578
3607
assert (inContext && " Need context for already-substituted opaque types" );
3579
3608
return inContext->getParentModule ()
3580
3609
->lookupConformance (replacementType, protocol);
@@ -3604,8 +3633,8 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3604
3633
3605
3634
// Check that we are allowed to substitute the underlying type into the
3606
3635
// context.
3607
- auto inContext = this ->inContext ;
3608
- auto isContextWholeModule = this ->isContextWholeModule ;
3636
+ auto inContext = this ->getContext () ;
3637
+ auto isContextWholeModule = this ->isWholeModule () ;
3609
3638
if (partialSubstTy.findIf (
3610
3639
[inContext, substitutionKind, isContextWholeModule](Type t) -> bool {
3611
3640
if (!canSubstituteTypeInto (t, inContext, substitutionKind,
@@ -3628,8 +3657,28 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3628
3657
3629
3658
// If the type still contains opaque types, recur.
3630
3659
if (substTy->hasOpaqueArchetype ()) {
3631
- return ::substOpaqueTypesWithUnderlyingTypes (
3632
- substRef, substTy, inContext, contextExpansion, isContextWholeModule);
3660
+ if (auto *alreadySeen = this ->seenDecls ) {
3661
+ // Detect substitution loops. If we find one, just bounce the original
3662
+ // type back to the caller. This substitution will fail at runtime
3663
+ // instead.
3664
+ if (!alreadySeen->insert (opaqueRoot->getDecl ()).second ) {
3665
+ return abstractRef;
3666
+ }
3667
+
3668
+ auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
3669
+ substRef, substTy, inContext, contextExpansion, isContextWholeModule,
3670
+ *alreadySeen);
3671
+ alreadySeen->erase (opaqueRoot->getDecl ());
3672
+ return res;
3673
+ } else {
3674
+ // We're the top of the stack for the recursion check. Allocate a set of
3675
+ // opaque result type decls we've already seen for the rest of the check.
3676
+ SmallPtrSet<OpaqueTypeDecl *, 8 > seenDecls;
3677
+ seenDecls.insert (opaqueRoot->getDecl ());
3678
+ return ::substOpaqueTypesWithUnderlyingTypesRec (
3679
+ substRef, substTy, inContext, contextExpansion, isContextWholeModule,
3680
+ seenDecls);
3681
+ }
3633
3682
}
3634
3683
return substRef;
3635
3684
}
0 commit comments