Skip to content

Commit ed7bd9b

Browse files
committed
Don't terminate ReplaceOpaqueTypesWithUnderlyingTypes if same opaque type is substituted with different substitution arguments.
A finite opaque type can still be composed of multiple references to the same declaration's opaque type. Change the occurs check here to account for both substitution map and opaque type decl instead of just the decl. Fixes rdar://88120984.
1 parent edf98aa commit ed7bd9b

File tree

3 files changed

+41
-13
lines changed

3 files changed

+41
-13
lines changed

include/swift/AST/Types.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5672,9 +5672,12 @@ enum class OpaqueSubstitutionKind {
56725672
/// archetypes with underlying types visible at a given resilience expansion
56735673
/// to their underlying types.
56745674
class ReplaceOpaqueTypesWithUnderlyingTypes {
5675+
public:
5676+
using SeenDecl = std::pair<OpaqueTypeDecl *, SubstitutionMap>;
5677+
private:
56755678
ResilienceExpansion contextExpansion;
56765679
llvm::PointerIntPair<const DeclContext *, 1, bool> inContextAndIsWholeModule;
5677-
llvm::SmallPtrSetImpl<OpaqueTypeDecl *> *seenDecls;
5680+
llvm::DenseSet<SeenDecl> *seenDecls;
56785681

56795682
public:
56805683
ReplaceOpaqueTypesWithUnderlyingTypes(const DeclContext *inContext,
@@ -5686,7 +5689,7 @@ class ReplaceOpaqueTypesWithUnderlyingTypes {
56865689

56875690
ReplaceOpaqueTypesWithUnderlyingTypes(
56885691
const DeclContext *inContext, ResilienceExpansion contextExpansion,
5689-
bool isWholeModuleContext, llvm::SmallPtrSetImpl<OpaqueTypeDecl *> &seen);
5692+
bool isWholeModuleContext, llvm::DenseSet<SeenDecl> &seen);
56905693

56915694
/// TypeSubstitutionFn
56925695
Type operator()(SubstitutableType *maybeOpaqueType) const;

lib/AST/Type.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3449,7 +3449,8 @@ ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution(
34493449

34503450
static Type substOpaqueTypesWithUnderlyingTypesRec(
34513451
Type ty, const DeclContext *inContext, ResilienceExpansion contextExpansion,
3452-
bool isWholeModuleContext, SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3452+
bool isWholeModuleContext,
3453+
llvm::DenseSet<ReplaceOpaqueTypesWithUnderlyingTypes::SeenDecl> &decls) {
34533454
ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion,
34543455
isWholeModuleContext, decls);
34553456
return ty.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
@@ -3513,7 +3514,7 @@ static bool canSubstituteTypeInto(Type ty, const DeclContext *dc,
35133514

35143515
ReplaceOpaqueTypesWithUnderlyingTypes::ReplaceOpaqueTypesWithUnderlyingTypes(
35153516
const DeclContext *inContext, ResilienceExpansion contextExpansion,
3516-
bool isWholeModuleContext, llvm::SmallPtrSetImpl<OpaqueTypeDecl *> &seen)
3517+
bool isWholeModuleContext, llvm::DenseSet<SeenDecl> &seen)
35173518
: contextExpansion(contextExpansion),
35183519
inContextAndIsWholeModule(inContext, isWholeModuleContext),
35193520
seenDecls(&seen) {}
@@ -3565,24 +3566,25 @@ operator()(SubstitutableType *maybeOpaqueType) const {
35653566

35663567
// If the type changed, but still contains opaque types, recur.
35673568
if (!substTy->isEqual(maybeOpaqueType) && substTy->hasOpaqueArchetype()) {
3569+
SeenDecl seenKey(opaqueRoot->getDecl(), opaqueRoot->getSubstitutions());
35683570
if (auto *alreadySeen = this->seenDecls) {
35693571
// Detect substitution loops. If we find one, just bounce the original
35703572
// type back to the caller. This substitution will fail at runtime
35713573
// instead.
3572-
if (!alreadySeen->insert(opaqueRoot->getDecl()).second) {
3574+
if (!alreadySeen->insert(seenKey).second) {
35733575
return maybeOpaqueType;
35743576
}
35753577

35763578
auto res = ::substOpaqueTypesWithUnderlyingTypesRec(
35773579
substTy, inContext, contextExpansion, isContextWholeModule,
35783580
*alreadySeen);
3579-
alreadySeen->erase(opaqueRoot->getDecl());
3581+
alreadySeen->erase(seenKey);
35803582
return res;
35813583
} else {
35823584
// We're the top of the stack for the recursion check. Allocate a set of
35833585
// opaque result type decls we've already seen for the rest of the check.
3584-
SmallPtrSet<OpaqueTypeDecl *, 8> seenDecls;
3585-
seenDecls.insert(opaqueRoot->getDecl());
3586+
llvm::DenseSet<SeenDecl> seenDecls;
3587+
seenDecls.insert(seenKey);
35863588
return ::substOpaqueTypesWithUnderlyingTypesRec(
35873589
substTy, inContext, contextExpansion, isContextWholeModule,
35883590
seenDecls);
@@ -3595,7 +3597,7 @@ operator()(SubstitutableType *maybeOpaqueType) const {
35953597
static ProtocolConformanceRef substOpaqueTypesWithUnderlyingTypesRec(
35963598
ProtocolConformanceRef ref, Type origType, const DeclContext *inContext,
35973599
ResilienceExpansion contextExpansion, bool isWholeModuleContext,
3598-
SmallPtrSetImpl<OpaqueTypeDecl *> &decls) {
3600+
llvm::DenseSet<ReplaceOpaqueTypesWithUnderlyingTypes::SeenDecl> &decls) {
35993601
ReplaceOpaqueTypesWithUnderlyingTypes replacer(inContext, contextExpansion,
36003602
isWholeModuleContext, decls);
36013603
return ref.subst(origType, replacer, replacer,
@@ -3679,24 +3681,26 @@ operator()(CanType maybeOpaqueType, Type replacementType,
36793681

36803682
// If the type still contains opaque types, recur.
36813683
if (substTy->hasOpaqueArchetype()) {
3684+
SeenDecl seenKey(opaqueRoot->getDecl(), opaqueRoot->getSubstitutions());
3685+
36823686
if (auto *alreadySeen = this->seenDecls) {
36833687
// Detect substitution loops. If we find one, just bounce the original
36843688
// type back to the caller. This substitution will fail at runtime
36853689
// instead.
3686-
if (!alreadySeen->insert(opaqueRoot->getDecl()).second) {
3690+
if (!alreadySeen->insert(seenKey).second) {
36873691
return abstractRef;
36883692
}
36893693

36903694
auto res = ::substOpaqueTypesWithUnderlyingTypesRec(
36913695
substRef, substTy, inContext, contextExpansion, isContextWholeModule,
36923696
*alreadySeen);
3693-
alreadySeen->erase(opaqueRoot->getDecl());
3697+
alreadySeen->erase(seenKey);
36943698
return res;
36953699
} else {
36963700
// We're the top of the stack for the recursion check. Allocate a set of
36973701
// opaque result type decls we've already seen for the rest of the check.
3698-
SmallPtrSet<OpaqueTypeDecl *, 8> seenDecls;
3699-
seenDecls.insert(opaqueRoot->getDecl());
3702+
llvm::DenseSet<SeenDecl> seenDecls;
3703+
seenDecls.insert(seenKey);
37003704
return ::substOpaqueTypesWithUnderlyingTypesRec(
37013705
substRef, substTy, inContext, contextExpansion, isContextWholeModule,
37023706
seenDecls);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-emit-sil -disable-availability-checking -verify %s
2+
3+
// rdar://88120984
4+
5+
protocol P {}
6+
7+
private struct Wrapped<T: P>: P { var x: T }
8+
9+
extension P {
10+
func wrapped() -> some P {
11+
return Wrapped(x: self)
12+
}
13+
}
14+
15+
class CWrap<T: P> { init(x: T) {} }
16+
17+
func foo<T: P>(x: T) {
18+
let y = x.wrapped().wrapped().wrapped().wrapped()
19+
20+
_ = CWrap(x: y)
21+
}

0 commit comments

Comments
 (0)