From b8d0ba9732573c56df5e53e3a9d1c54ba3222116 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 23 Oct 2025 14:56:51 -0400 Subject: [PATCH 1/5] AST: Fix substOpaqueTypesWithUnderlyingTypes() to set SubstFlags::PreservePackExpansionLevel The "iterate until fixed point" would cause an infinite loop where we wrap type parameter packs in a PackElementType and increase the level forever otherwise. This was a regression from commit 103428fe804bdbfe9a0a74d201bd85996b484523. Fixes rdar://160804717. --- lib/AST/TypeSubstitution.cpp | 3 ++- test/Generics/rdar160804717.swift | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/Generics/rdar160804717.swift diff --git a/lib/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index 3eaf3cd55e5ab..ff5cffca7bcb5 100644 --- a/lib/AST/TypeSubstitution.cpp +++ b/lib/AST/TypeSubstitution.cpp @@ -1093,7 +1093,8 @@ ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes( context.getContext(), context.getResilienceExpansion(), context.isWholeModuleContext()); InFlightSubstitution IFS(replacer, replacer, - SubstFlags::SubstituteOpaqueArchetypes); + SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::PreservePackExpansionLevel); auto substRef = ref.subst(IFS); diff --git a/test/Generics/rdar160804717.swift b/test/Generics/rdar160804717.swift new file mode 100644 index 0000000000000..8b8429cc390cf --- /dev/null +++ b/test/Generics/rdar160804717.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -emit-ir %s -target %target-swift-5.9-abi-triple +// REQUIRES: objc_interop + +// This used to trigger an infinite loop in conformance substitution +// when emitting the opaque type descriptor in IRGen. + +// rdar://160804717 + +import SwiftUI + +public struct CategorySplitView: View { + let titleKey: LocalizedStringKey + let groups: (repeat each Destination) + + public var body: some View { + TupleView((repeat CategoryGroupSidebar(group: each groups))).navigationTitle(titleKey) + } +} + +public struct CategoryGroupSidebar: View { + let group: Destination + + public var body: some View { + Text("Hi") + } +} + From da493101161038360abb75354d9c22034821a9da Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 23 Oct 2025 14:57:31 -0400 Subject: [PATCH 2/5] Sema: Fix findMissingGenericRequirementForSolutionFix() to set SubstFlags::PreservePackExpansionLevel --- lib/Sema/TypeCheckProtocol.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 3a9ac674a8a45..9f4bf072c2d25 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1047,7 +1047,8 @@ findMissingGenericRequirementForSolutionFix( return env->mapTypeIntoContext(gp); }, - LookUpConformanceInModule()); + LookUpConformanceInModule(), + SubstFlags::PreservePackExpansionLevel); }; type = getTypeInConformanceContext(type); From 866737ed71acd7022b6b25c06e7ab3bc9f014926 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 23 Oct 2025 14:57:52 -0400 Subject: [PATCH 3/5] SIL: Fix substWitnessConformance() to set SubstFlags::PreservePackExpansionLevel --- lib/SIL/IR/SILTypeSubstitution.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/SIL/IR/SILTypeSubstitution.cpp b/lib/SIL/IR/SILTypeSubstitution.cpp index dab536f664c83..2e4d6a4ddc5d9 100644 --- a/lib/SIL/IR/SILTypeSubstitution.cpp +++ b/lib/SIL/IR/SILTypeSubstitution.cpp @@ -273,9 +273,10 @@ class SILTypeSubstituter : // Substitute the underlying conformance of opaque type archetypes if we // should look through opaque archetypes. if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - auto substType = IFS.withNewOptions(std::nullopt, [&] { - return selfType.subst(IFS)->getCanonicalType(); - }); + auto substType = IFS.withNewOptions( + SubstFlags::PreservePackExpansionLevel, [&] { + return selfType.subst(IFS)->getCanonicalType(); + }); if (substType->hasOpaqueArchetype()) { substConformance = substOpaqueTypesWithUnderlyingTypes( substConformance, typeExpansionContext); From 12ce32ef09f162d77f891f91cbd355484259b7ff Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 23 Oct 2025 15:03:50 -0400 Subject: [PATCH 4/5] AST: Early return from substOpaqueTypesWithUnderlyingTypes() overloads if nothing to do --- lib/AST/SubstitutionMap.cpp | 12 ++++++++++++ lib/AST/TypeSubstitution.cpp | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index 772f2976ea358..e311b681ea5e7 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -620,6 +620,18 @@ bool SubstitutionMap::isIdentity() const { SubstitutionMap swift::substOpaqueTypesWithUnderlyingTypes( SubstitutionMap subs, TypeExpansionContext context) { + if (!context.shouldLookThroughOpaqueTypeArchetypes()) + return subs; + + if (!subs.getRecursiveProperties().hasOpaqueArchetype() && + !llvm::any_of(subs.getConformances(), + [&](ProtocolConformanceRef ref) { + return (!ref.isInvalid() && + ref.getType()->hasOpaqueArchetype()); + })) { + return subs; + } + ReplaceOpaqueTypesWithUnderlyingTypes replacer( context.getContext(), context.getResilienceExpansion(), context.isWholeModuleContext()); diff --git a/lib/AST/TypeSubstitution.cpp b/lib/AST/TypeSubstitution.cpp index ff5cffca7bcb5..410f633650194 100644 --- a/lib/AST/TypeSubstitution.cpp +++ b/lib/AST/TypeSubstitution.cpp @@ -1089,6 +1089,13 @@ swift::substOpaqueTypesWithUnderlyingTypes(CanType ty, ProtocolConformanceRef swift::substOpaqueTypesWithUnderlyingTypes( ProtocolConformanceRef ref, TypeExpansionContext context) { + if (ref.isInvalid()) + return ref; + + if (!context.shouldLookThroughOpaqueTypeArchetypes() || + !ref.getType()->hasOpaqueArchetype()) + return ref; + ReplaceOpaqueTypesWithUnderlyingTypes replacer( context.getContext(), context.getResilienceExpansion(), context.isWholeModuleContext()); From 41ff7383d95f4968db5959783189b05258212851 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 23 Oct 2025 15:11:39 -0400 Subject: [PATCH 5/5] IRGen: Clean up opaque type specialization wrappers --- lib/IRGen/GenProto.cpp | 4 ++-- lib/IRGen/GenReflection.cpp | 7 +++--- lib/IRGen/IRGenModule.h | 7 +++--- lib/IRGen/MetadataRequest.cpp | 41 ++++++++++------------------------- lib/IRGen/Outlining.cpp | 2 +- 5 files changed, 21 insertions(+), 40 deletions(-) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 96b2960d37fae..0f394856bef32 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3761,8 +3761,8 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF, // Look through any opaque types we're allowed to. if (srcType->hasOpaqueArchetype()) { - std::tie(srcType, conformance) = - IGF.IGM.substOpaqueTypesWithUnderlyingTypes(srcType, conformance); + srcType = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(srcType); + conformance = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(conformance); } // If we don't have concrete conformance information, the type must be diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 848731fe1088e..8b0f5860a55a4 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -583,8 +583,7 @@ std::pair IRGenModule::getLoweredTypeRef(SILType loweredType, CanGenericSignature genericSig, MangledTypeRefRole role) { - auto substTy = - substOpaqueTypesWithUnderlyingTypes(loweredType, genericSig); + auto substTy = substOpaqueTypesWithUnderlyingTypes(loweredType); auto type = substTy.getASTType(); return getTypeRefImpl(*this, type, genericSig, role); } @@ -600,8 +599,8 @@ IRGenModule::emitWitnessTableRefString(CanType type, ProtocolConformanceRef conformance, GenericSignature origGenericSig, bool shouldSetLowBit) { - std::tie(type, conformance) - = substOpaqueTypesWithUnderlyingTypes(type, conformance); + type = substOpaqueTypesWithUnderlyingTypes(type); + conformance = substOpaqueTypesWithUnderlyingTypes(conformance); auto origType = type; auto genericSig = origGenericSig.getCanonicalSignature(); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 736b66bdbd67b..68f7076feb006 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1162,10 +1162,9 @@ class IRGenModule { CanType getRuntimeReifiedType(CanType type); Type substOpaqueTypesWithUnderlyingTypes(Type type); CanType substOpaqueTypesWithUnderlyingTypes(CanType type); - SILType substOpaqueTypesWithUnderlyingTypes(SILType type, CanGenericSignature genericSig); - std::pair - substOpaqueTypesWithUnderlyingTypes(CanType type, - ProtocolConformanceRef conformance); + SILType substOpaqueTypesWithUnderlyingTypes(SILType type); + ProtocolConformanceRef + substOpaqueTypesWithUnderlyingTypes(ProtocolConformanceRef conformance); bool isResilient(NominalTypeDecl *decl, ResilienceExpansion expansion, ClassDecl *asViewedFromRootClass = nullptr); diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 3ef16b323c058..92c10012361c4 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -494,12 +494,8 @@ CanType IRGenModule::getRuntimeReifiedType(CanType type) { Type IRGenModule::substOpaqueTypesWithUnderlyingTypes(Type type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. - if (type->hasOpaqueArchetype()) { - auto context = getMaximalTypeExpansionContext(); - return swift::substOpaqueTypesWithUnderlyingTypes(type, context); - } - - return type; + auto context = getMaximalTypeExpansionContext(); + return swift::substOpaqueTypesWithUnderlyingTypes(type, context); } CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { @@ -507,33 +503,20 @@ CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) { ->getCanonicalType(); } -SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes( - SILType type, CanGenericSignature genericSig) { +SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes(SILType type) { // Substitute away opaque types whose underlying types we're allowed to // assume are constant. - if (type.getASTType()->hasOpaqueArchetype()) { - auto context = getMaximalTypeExpansionContext(); - return SILType::getPrimitiveType( - swift::substOpaqueTypesWithUnderlyingTypes(type.getASTType(), context), - type.getCategory()); - } - - return type; + auto context = getMaximalTypeExpansionContext(); + return SILType::getPrimitiveType( + swift::substOpaqueTypesWithUnderlyingTypes(type.getASTType(), context), + type.getCategory()); } -std::pair -IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type, - ProtocolConformanceRef conformance) { - // Substitute away opaque types whose underlying types we're allowed to - // assume are constant. - if (type->hasOpaqueArchetype()) { - auto context = getMaximalTypeExpansionContext(); - return std::make_pair( - swift::substOpaqueTypesWithUnderlyingTypes(type, context), - swift::substOpaqueTypesWithUnderlyingTypes(conformance, context)); - } - - return std::make_pair(type, conformance); +ProtocolConformanceRef +IRGenModule::substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef conformance) { + auto context = getMaximalTypeExpansionContext(); + return swift::substOpaqueTypesWithUnderlyingTypes(conformance, context); } diff --git a/lib/IRGen/Outlining.cpp b/lib/IRGen/Outlining.cpp index 96df2cc45c6c8..1b84a1256286a 100644 --- a/lib/IRGen/Outlining.cpp +++ b/lib/IRGen/Outlining.cpp @@ -68,7 +68,7 @@ void OutliningMetadataCollector::collectTypeMetadata(SILType ty) { } // Substitute opaque types if allowed. - ty = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(ty, CanGenericSignature()); + ty = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(ty); collectTypeMetadataForLayout(ty); collectTypeMetadataForDeinit(ty);