@@ -3333,6 +3333,7 @@ getArchetypeAndRootOpaqueArchetype(Type maybeOpaqueType) {
3333
3333
OpaqueSubstitutionKind
3334
3334
ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution (
3335
3335
OpaqueTypeDecl *opaque) const {
3336
+ const auto *inContext = getContext ();
3336
3337
auto inModule = inContext ? inContext->getParentModule ()
3337
3338
: opaque->getParentModule ();
3338
3339
return shouldPerformSubstitution (opaque, inModule, contextExpansion);
@@ -3376,12 +3377,11 @@ ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
3376
3377
return OpaqueSubstitutionKind::SubstituteNonResilientModule;
3377
3378
}
3378
3379
3379
- static Type
3380
- substOpaqueTypesWithUnderlyingTypes (Type ty, const DeclContext *inContext,
3381
- ResilienceExpansion contextExpansion,
3382
- bool isWholeModuleContext) {
3380
+ static Type substOpaqueTypesWithUnderlyingTypesRec (
3381
+ Type ty, const DeclContext *inContext, ResilienceExpansion contextExpansion,
3382
+ bool isWholeModuleContext, SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3383
3383
ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
3384
- isWholeModuleContext);
3384
+ isWholeModuleContext, decls );
3385
3385
return ty.subst (replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
3386
3386
}
3387
3387
@@ -3441,6 +3441,13 @@ static bool canSubstituteTypeInto(Type ty, const DeclContext *dc,
3441
3441
llvm_unreachable (" invalid subsitution kind" );
3442
3442
}
3443
3443
3444
+ ReplaceOpaqueTypesWithUnderlyingTypes::ReplaceOpaqueTypesWithUnderlyingTypes (
3445
+ const DeclContext *inContext, ResilienceExpansion contextExpansion,
3446
+ bool isWholeModuleContext, llvm::SmallPtrSetImpl<OpaqueTypeDecl *> &seen)
3447
+ : contextExpansion(contextExpansion),
3448
+ inContextAndIsWholeModule(inContext, isWholeModuleContext),
3449
+ seenDecls(&seen) {}
3450
+
3444
3451
Type ReplaceOpaqueTypesWithUnderlyingTypes::
3445
3452
operator ()(SubstitutableType *maybeOpaqueType) const {
3446
3453
auto archetypeAndRoot = getArchetypeAndRootOpaqueArchetype (maybeOpaqueType);
@@ -3468,8 +3475,8 @@ operator()(SubstitutableType *maybeOpaqueType) const {
3468
3475
3469
3476
// Check that we are allowed to substitute the underlying type into the
3470
3477
// context.
3471
- auto inContext = this ->inContext ;
3472
- auto isContextWholeModule = this ->isContextWholeModule ;
3478
+ auto inContext = this ->getContext () ;
3479
+ auto isContextWholeModule = this ->isWholeModule () ;
3473
3480
if (inContext &&
3474
3481
partialSubstTy.findIf (
3475
3482
[inContext, substitutionKind, isContextWholeModule](Type t) -> bool {
@@ -3488,18 +3495,39 @@ operator()(SubstitutableType *maybeOpaqueType) const {
3488
3495
3489
3496
// If the type changed, but still contains opaque types, recur.
3490
3497
if (!substTy->isEqual (maybeOpaqueType) && substTy->hasOpaqueArchetype ()) {
3491
- return ::substOpaqueTypesWithUnderlyingTypes (
3492
- substTy, inContext, contextExpansion, isContextWholeModule);
3498
+ if (auto *alreadySeen = this ->seenDecls ) {
3499
+ // Detect substitution loops. If we find one, just bounce the original
3500
+ // type back to the caller. This substitution will fail at runtime
3501
+ // instead.
3502
+ if (!alreadySeen->insert (opaqueRoot->getDecl ()).second ) {
3503
+ return maybeOpaqueType;
3504
+ }
3505
+
3506
+ auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
3507
+ substTy, inContext, contextExpansion, isContextWholeModule,
3508
+ *alreadySeen);
3509
+ alreadySeen->erase (opaqueRoot->getDecl ());
3510
+ return res;
3511
+ } else {
3512
+ // We're the top of the stack for the recursion check. Allocate a set of
3513
+ // opaque result type decls we've already seen for the rest of the check.
3514
+ SmallPtrSet<OpaqueTypeDecl *, 8 > seenDecls;
3515
+ seenDecls.insert (opaqueRoot->getDecl ());
3516
+ return ::substOpaqueTypesWithUnderlyingTypesRec (
3517
+ substTy, inContext, contextExpansion, isContextWholeModule,
3518
+ seenDecls);
3519
+ }
3493
3520
}
3494
3521
3495
3522
return substTy;
3496
3523
}
3497
3524
3498
- static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypes (
3525
+ static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec (
3499
3526
ProtocolConformanceRef ref, Type origType, const DeclContext *inContext,
3500
- ResilienceExpansion contextExpansion, bool isWholeModuleContext) {
3527
+ ResilienceExpansion contextExpansion, bool isWholeModuleContext,
3528
+ SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3501
3529
ReplaceOpaqueTypesWithUnderlyingTypes replacer (inContext, contextExpansion,
3502
- isWholeModuleContext);
3530
+ isWholeModuleContext, decls );
3503
3531
return ref.subst (origType, replacer, replacer,
3504
3532
SubstFlags::SubstituteOpaqueArchetypes);
3505
3533
}
@@ -3527,6 +3555,7 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3527
3555
// SIL type lowering may have already substituted away the opaque type, in
3528
3556
// which case we'll end up "substituting" the same type.
3529
3557
if (maybeOpaqueType->isEqual (replacementType)) {
3558
+ const auto *inContext = getContext ();
3530
3559
assert (inContext && " Need context for already-substituted opaque types" );
3531
3560
return inContext->getParentModule ()
3532
3561
->lookupConformance (replacementType, protocol);
@@ -3556,8 +3585,8 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3556
3585
3557
3586
// Check that we are allowed to substitute the underlying type into the
3558
3587
// context.
3559
- auto inContext = this ->inContext ;
3560
- auto isContextWholeModule = this ->isContextWholeModule ;
3588
+ auto inContext = this ->getContext () ;
3589
+ auto isContextWholeModule = this ->isWholeModule () ;
3561
3590
if (partialSubstTy.findIf (
3562
3591
[inContext, substitutionKind, isContextWholeModule](Type t) -> bool {
3563
3592
if (!canSubstituteTypeInto (t, inContext, substitutionKind,
@@ -3580,8 +3609,28 @@ operator()(CanType maybeOpaqueType, Type replacementType,
3580
3609
3581
3610
// If the type still contains opaque types, recur.
3582
3611
if (substTy->hasOpaqueArchetype ()) {
3583
- return ::substOpaqueTypesWithUnderlyingTypes (
3584
- substRef, substTy, inContext, contextExpansion, isContextWholeModule);
3612
+ if (auto *alreadySeen = this ->seenDecls ) {
3613
+ // Detect substitution loops. If we find one, just bounce the original
3614
+ // type back to the caller. This substitution will fail at runtime
3615
+ // instead.
3616
+ if (!alreadySeen->insert (opaqueRoot->getDecl ()).second ) {
3617
+ return abstractRef;
3618
+ }
3619
+
3620
+ auto res = ::substOpaqueTypesWithUnderlyingTypesRec (
3621
+ substRef, substTy, inContext, contextExpansion, isContextWholeModule,
3622
+ *alreadySeen);
3623
+ alreadySeen->erase (opaqueRoot->getDecl ());
3624
+ return res;
3625
+ } else {
3626
+ // We're the top of the stack for the recursion check. Allocate a set of
3627
+ // opaque result type decls we've already seen for the rest of the check.
3628
+ SmallPtrSet<OpaqueTypeDecl *, 8 > seenDecls;
3629
+ seenDecls.insert (opaqueRoot->getDecl ());
3630
+ return ::substOpaqueTypesWithUnderlyingTypesRec (
3631
+ substRef, substTy, inContext, contextExpansion, isContextWholeModule,
3632
+ seenDecls);
3633
+ }
3585
3634
}
3586
3635
return substRef;
3587
3636
}
0 commit comments