From c1ce0d72c2d7554225dbc99a51befc2df9015831 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 9 May 2024 12:39:27 -0400 Subject: [PATCH 1/6] SIL: Store captured environments in SILFunction --- include/swift/SIL/SILFunction.h | 44 +++++++++++++++++++++++++++--- include/swift/SIL/TypeLowering.h | 2 +- lib/SIL/IR/SILFunction.cpp | 14 ++++------ lib/SIL/IR/TypeLowering.cpp | 10 +++---- lib/SILGen/SILGen.cpp | 5 ++-- lib/Serialization/SerializeSIL.cpp | 3 ++ 6 files changed, 56 insertions(+), 22 deletions(-) diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index f91966dfe5a46..9c42fcdfb5ed4 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -237,6 +237,9 @@ class SILFunction /// The context archetypes of the function. GenericEnvironment *GenericEnv = nullptr; + /// Captured local generic environments. + ArrayRef CapturedEnvs; + /// The information about specialization. /// Only set if this function is a specialization of another function. const GenericSpecializationInformation *SpecializationInfo = nullptr; @@ -1292,9 +1295,21 @@ class SILFunction GenericEnvironment *getGenericEnvironment() const { return GenericEnv; } + + /// Return any captured local generic environments, currently used for pack + /// element environments only. After SILGen, these are rewritten into + /// primary archetypes. + ArrayRef getCapturedEnvironments() const { + return CapturedEnvs; + } + + void setGenericEnvironment(GenericEnvironment *env); + void setGenericEnvironment(GenericEnvironment *env, - SubstitutionMap forwardingSubs=SubstitutionMap()) { + ArrayRef capturedEnvs, + SubstitutionMap forwardingSubs) { GenericEnv = env; + CapturedEnvs = capturedEnvs; ForwardingSubMap = forwardingSubs; } @@ -1324,9 +1339,30 @@ class SILFunction /// responsibility of the caller. void eraseAllBlocks(); - /// Return the identity substitutions necessary to forward this call if it is - /// generic. - SubstitutionMap getForwardingSubstitutionMap(); + /// A substitution map that sends the generic parameters of the invocation + /// generic signature to some combination of primar and local archetypes. + /// + /// CAUTION: If this is a SILFunction that captures pack element environments, + /// then at SILGen time, this is not actually the forwarding substitution map + /// of the SILFunction's generic environment. This is because: + /// + /// 1) The SILFunction's generic signature includes extra generic parameters, + /// to model captured pack elements; + /// 2) The SILFunction's generic environment is the AST generic environment, + /// so it's based on the original generic signature; + /// 3) SILGen uses this AST generic environment together with local archetypes + /// for lowering SIL instructions. + /// + /// Therefore, the SILFunction's forwarding substitution map takes the extended + /// generic signature (1). It maps the original generic parameters to the + /// archetypes of (2), and the extended generic parameters to the local archetypes + /// of (3). + /// + /// After SILGen, all archetypes are re-instantiated inside the SIL function, + /// and the forwarding substitution map and generic environment then align. + SubstitutionMap getForwardingSubstitutionMap() const { + return ForwardingSubMap; + } /// Returns true if this SILFunction must be a defer statement. /// diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index 672d8efb1d131..069636d5e7f79 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -1080,7 +1080,7 @@ class TypeConverter { /// sends the generic parameters of the function's interface type into /// archetypes, which will either be primary archetypes from this /// environment, or local archetypes captured by this function. - std::pair + std::tuple, SubstitutionMap> getForwardingSubstitutionsForLowering(SILDeclRef constant); /// Returns the SIL type of a constant reference. diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index ce39bc271045e..3fa311f56c954 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -245,7 +245,6 @@ void SILFunction::init( "function type has open type parameters"); this->LoweredType = LoweredType; - this->GenericEnv = genericEnv; this->SpecializationInfo = nullptr; this->EntryCount = entryCount; this->Availability = AvailabilityContext::alwaysAvailable(); @@ -282,6 +281,7 @@ void SILFunction::init( assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); setDebugScope(DebugScope); + setGenericEnvironment(genericEnv); } SILFunction::~SILFunction() { @@ -1033,14 +1033,10 @@ void SILFunction::eraseAllBlocks() { BlockList.clear(); } -SubstitutionMap SILFunction::getForwardingSubstitutionMap() { - if (ForwardingSubMap) - return ForwardingSubMap; - - if (auto *env = getGenericEnvironment()) - ForwardingSubMap = env->getForwardingSubstitutionMap(); - - return ForwardingSubMap; +void SILFunction::setGenericEnvironment(GenericEnvironment *env) { + setGenericEnvironment(env, ArrayRef(), + env ? env->getForwardingSubstitutionMap() + : SubstitutionMap()); } bool SILFunction::shouldVerifyOwnership() const { diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 287031086e118..950b87d25cb12 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -4116,17 +4116,15 @@ TypeConverter::getConstantGenericEnvironment(SILDeclRef c) { .genericSig.getGenericEnvironment(); } -std::pair +std::tuple, SubstitutionMap> TypeConverter::getForwardingSubstitutionsForLowering(SILDeclRef constant) { auto sig = getGenericSignatureWithCapturedEnvironments(constant); - GenericEnvironment *genericEnv = nullptr; + auto *genericEnv = sig.baseGenericSig.getGenericEnvironment(); SubstitutionMap forwardingSubs; - if (sig.baseGenericSig) { - genericEnv = sig.baseGenericSig.getGenericEnvironment(); + if (sig.baseGenericSig) forwardingSubs = genericEnv->getForwardingSubstitutionMap(); - } if (!sig.capturedEnvs.empty()) { assert(sig.genericSig && !sig.genericSig->isEqual(sig.baseGenericSig)); @@ -4135,7 +4133,7 @@ TypeConverter::getForwardingSubstitutionsForLowering(SILDeclRef constant) { forwardingSubs, sig.genericSig, sig.capturedEnvs); } - return std::make_pair(genericEnv, forwardingSubs); + return std::make_tuple(genericEnv, sig.capturedEnvs, forwardingSubs); } SILType TypeConverter::getSubstitutedStorageType(TypeExpansionContext context, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 1707a49df384d..11a816b6b7057 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1224,8 +1224,9 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F, assert(F->empty() && "already emitted function?!"); if (F->getLoweredFunctionType()->isPolymorphic()) { - auto pair = Types.getForwardingSubstitutionsForLowering(constant); - F->setGenericEnvironment(pair.first, pair.second); + auto [genericEnv, capturedEnvs, forwardingSubs] + = Types.getForwardingSubstitutionsForLowering(constant); + F->setGenericEnvironment(genericEnv, capturedEnvs, forwardingSubs); } // If we have global actor isolation for our constant, put the isolation onto diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 2a31a3919767c..36b99c4382181 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -460,6 +460,9 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { Linkage = addExternalToLinkage(Linkage); } + assert(F.getCapturedEnvironments().empty() && + "Captured local environments should not survive past SILGen"); + // If we have a body, we might have a generic environment. GenericSignatureID genericSigID = 0; if (!NoBody) From 5ec98033a4c304cfcd4e0259dde6d735725aa4fe Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 9 May 2024 12:39:50 -0400 Subject: [PATCH 2/6] AST: Factor out MapIntoLocalArchetypeContext from buildSubstitutionMapWithCapturedEnvironments() --- .../AST/LocalArchetypeRequirementCollector.h | 11 +++++++ .../LocalArchetypeRequirementCollector.cpp | 33 ++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/LocalArchetypeRequirementCollector.h b/include/swift/AST/LocalArchetypeRequirementCollector.h index 3dde78acc21e2..7b9a933d5fb77 100644 --- a/include/swift/AST/LocalArchetypeRequirementCollector.h +++ b/include/swift/AST/LocalArchetypeRequirementCollector.h @@ -53,6 +53,17 @@ struct MapLocalArchetypesOutOfContext { Type operator()(SubstitutableType *type) const; }; +struct MapIntoLocalArchetypeContext { + GenericEnvironment *baseGenericEnv; + ArrayRef capturedEnvs; + + MapIntoLocalArchetypeContext(GenericEnvironment *baseGenericEnv, + ArrayRef capturedEnvs) + : baseGenericEnv(baseGenericEnv), capturedEnvs(capturedEnvs) {} + + Type operator()(SubstitutableType *type) const; +}; + GenericSignature buildGenericSignatureWithCapturedEnvironments( ASTContext &ctx, GenericSignature sig, diff --git a/lib/AST/LocalArchetypeRequirementCollector.cpp b/lib/AST/LocalArchetypeRequirementCollector.cpp index a1ea104379af9..b0d1fcf5f128a 100644 --- a/lib/AST/LocalArchetypeRequirementCollector.cpp +++ b/lib/AST/LocalArchetypeRequirementCollector.cpp @@ -194,6 +194,28 @@ Type MapLocalArchetypesOutOfContext::operator()(SubstitutableType *type) const { llvm_unreachable("Fell off the end"); } +static Type mapIntoLocalContext(GenericTypeParamType *param, unsigned baseDepth, + ArrayRef capturedEnvs) { + assert(!param->isParameterPack()); + unsigned envIndex = param->getDepth() - baseDepth; + assert(envIndex < capturedEnvs.size()); + auto *capturedEnv = capturedEnvs[envIndex]; + auto localInterfaceType = capturedEnv->getGenericSignature() + .getInnermostGenericParams()[param->getIndex()]; + assert(localInterfaceType->getIndex() == param->getIndex()); + return capturedEnvs[envIndex]->mapTypeIntoContext(localInterfaceType); +} + +Type MapIntoLocalArchetypeContext::operator()(SubstitutableType *type) const { + unsigned baseDepth = baseGenericEnv->getGenericSignature().getNextDepth(); + + auto param = cast(type); + if (param->getDepth() >= baseDepth) + return mapIntoLocalContext(param, baseDepth, capturedEnvs); + + return baseGenericEnv->mapTypeIntoContext(param); +} + /// Given a substitution map for a call to a local function or closure, extend /// it to include all captured element archetypes; they become primary archetypes /// inside the body of the function. @@ -215,15 +237,8 @@ swift::buildSubstitutionMapWithCapturedEnvironments( genericSigWithCaptures, [&](SubstitutableType *type) -> Type { auto param = cast(type); - if (param->getDepth() >= baseDepth) { - assert(!param->isParameterPack()); - unsigned envIndex = param->getDepth() - baseDepth; - assert(envIndex < capturedEnvs.size()); - auto *capturedEnv = capturedEnvs[envIndex]; - auto localInterfaceType = capturedEnv->getGenericSignature() - .getInnermostGenericParams()[param->getIndex()]; - return capturedEnvs[envIndex]->mapTypeIntoContext(localInterfaceType); - } + if (param->getDepth() >= baseDepth) + return mapIntoLocalContext(param, baseDepth, capturedEnvs); return Type(type).subst(baseSubMap); }, [&](CanType origType, Type substType, From 7d0e041572ab6dbd6ce292b28156e0c4e2095746 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 10 May 2024 14:07:21 -0400 Subject: [PATCH 3/6] AutoDiff: Local workaround for invariant violation --- lib/SILOptimizer/Differentiation/PullbackCloner.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index 94ea71c509d62..cec690c2cc63b 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -225,7 +225,11 @@ class PullbackCloner::Implementation final getPullback().getLoweredFunctionType()->getSubstGenericSignature()); auto remappedSILType = SILType::getPrimitiveType(remappedType, ty.getCategory()); - return getPullback().mapTypeIntoContext(remappedSILType); + // FIXME: Sometimes getPullback() doesn't have a generic environment, in which + // case callers are apparently happy to receive an interface type. + if (getPullback().getGenericEnvironment()) + return getPullback().mapTypeIntoContext(remappedSILType); + return remappedSILType; } std::optional getTangentSpace(CanType type) { From 60615b8a528cb8ccf069661101d4aa92e94a9e98 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 9 May 2024 12:54:13 -0400 Subject: [PATCH 4/6] SIL: Handle captured environments in SILFunction::mapTypeIntoContext() --- lib/SIL/IR/SILFunction.cpp | 40 ++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 3fa311f56c954..d1f19eae0bef9 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -17,6 +17,7 @@ #include "swift/AST/Expr.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IRGenOptions.h" +#include "swift/AST/LocalArchetypeRequirementCollector.h" #include "swift/AST/Module.h" #include "swift/AST/Stmt.h" #include "swift/Basic/OptimizationMode.h" @@ -492,24 +493,51 @@ bool SILFunction::shouldOptimize() const { } Type SILFunction::mapTypeIntoContext(Type type) const { - return GenericEnvironment::mapTypeIntoContext( - getGenericEnvironment(), type); + assert(!type->hasPrimaryArchetype()); + + if (GenericEnv) { + // The complication here is that we sometimes call this with an AST interface + // type, which might contain element archetypes, if it was the interface type + // of a closure or local variable. + if (type->hasElementArchetype()) + return GenericEnv->mapTypeIntoContext(type); + + // Otherwise, assume we have an interface type for the "combined" captured + // environment. + return type.subst(MapIntoLocalArchetypeContext(GenericEnv, CapturedEnvs), + LookUpConformanceInModule(Module.getSwiftModule()), + SubstFlags::AllowLoweredTypes | + SubstFlags::PreservePackExpansionLevel); + } + + assert(!type->hasTypeParameter()); + return type; } SILType SILFunction::mapTypeIntoContext(SILType type) const { - if (auto *genericEnv = getGenericEnvironment()) - return genericEnv->mapTypeIntoContext(getModule(), type); + assert(!type.hasPrimaryArchetype()); + + if (GenericEnv) { + auto genericSig = GenericEnv->getGenericSignature().getCanonicalSignature(); + return type.subst(Module, + MapIntoLocalArchetypeContext(GenericEnv, CapturedEnvs), + LookUpConformanceInModule(Module.getSwiftModule()), + genericSig, + SubstFlags::PreservePackExpansionLevel); + } + + assert(!type.hasTypeParameter()); return type; } SILType GenericEnvironment::mapTypeIntoContext(SILModule &M, SILType type) const { - assert(!type.hasArchetype()); + assert(!type.hasPrimaryArchetype()); auto genericSig = getGenericSignature().getCanonicalSignature(); return type.subst(M, QueryInterfaceTypeSubstitutions(this), - LookUpConformanceInSignature(genericSig.getPointer()), + LookUpConformanceInModule(M.getSwiftModule()), genericSig, SubstFlags::PreservePackExpansionLevel); } From a40a6e215d113a585cb815cb2a38acd5ff1846fb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 16 May 2024 18:11:59 -0400 Subject: [PATCH 5/6] SIL: Fix assertion failure after deleting instructions that contain unresolved local archetypes We maintained a counter of the number of pending local archetypes that had not yet been defined. However, if an instruction that references a pending local archetype was deleted before the local archetype was defined, the counter would never decrement. Before reading the counter value, garbage collect any inserted placeholders that have no uses. These correspond to pending local archetypes that are no longer in use and will never be defined. --- lib/SIL/IR/SILModule.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 11e2da3d5261e..1468026702a9d 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -688,6 +688,23 @@ SILValue SILModule::getRootLocalArchetypeDef(CanLocalArchetypeType archetype, } bool SILModule::hasUnresolvedLocalArchetypeDefinitions() { + // Garbage collect dead placeholders first. + llvm::DenseMap newLocalArchetypeDefs; + + for (auto pair : RootLocalArchetypeDefs) { + if (auto *placeholder = dyn_cast(pair.second)) { + if (placeholder->use_empty()) { + --numUnresolvedLocalArchetypes; + ::delete placeholder; + continue; + } + } + + newLocalArchetypeDefs.insert(pair); + } + + std::swap(newLocalArchetypeDefs, RootLocalArchetypeDefs); + return numUnresolvedLocalArchetypes != 0; } @@ -753,6 +770,8 @@ void SILModule::notifyAddedInstruction(SILInstruction *inst) { auto *placeholder = cast(val); placeholder->replaceAllUsesWith(dependency); ::delete placeholder; + + assert(numUnresolvedLocalArchetypes > 0); numUnresolvedLocalArchetypes--; } val = dependency; From cf2766336ac6084238fc45be4f123a188778efa1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 16 May 2024 18:11:30 -0400 Subject: [PATCH 6/6] ASTMangler: Support closures whose types contain local archetypes --- include/swift/AST/ASTMangler.h | 3 ++- lib/AST/ASTMangler.cpp | 36 ++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 0e3ea7ca6862e..1434c86b3e051 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -656,7 +656,8 @@ class ASTMangler : public Mangler { void appendClosureEntity(const AbstractClosureExpr *closure); void appendClosureComponents(Type Ty, unsigned discriminator, bool isImplicit, - const DeclContext *parentContext); + const DeclContext *parentContext, + ArrayRef capturedEnvs); void appendDefaultArgumentEntity(const DeclContext *ctx, unsigned index); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 000b6fdfad88b..324c29d4e9974 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -25,6 +25,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" +#include "swift/AST/LocalArchetypeRequirementCollector.h" #include "swift/AST/MacroDiscriminatorContext.h" #include "swift/AST/Module.h" #include "swift/AST/Ownership.h" @@ -3733,29 +3734,48 @@ void ASTMangler::appendAssociatedTypeName(DependentMemberType *dmt, void ASTMangler::appendClosureEntity( const SerializedAbstractClosureExpr *closure) { + assert(!closure->getType()->hasLocalArchetype() && + "Not enough information here to handle this case"); + appendClosureComponents(closure->getType(), closure->getDiscriminator(), - closure->isImplicit(), closure->getParent()); + closure->isImplicit(), closure->getParent(), + ArrayRef()); } void ASTMangler::appendClosureEntity(const AbstractClosureExpr *closure) { - appendClosureComponents(closure->getType(), closure->getDiscriminator(), - isa(closure), closure->getParent()); + ArrayRef capturedEnvs; + + auto type = closure->getType(); + + // FIXME: CodeCompletionResultBuilder calls printValueDeclUSR() but the + // closure hasn't been type checked yet. + if (!type) + type = ErrorType::get(closure->getASTContext()); + + if (type->hasLocalArchetype()) + capturedEnvs = closure->getCaptureInfo().getGenericEnvironments(); + + appendClosureComponents(type, closure->getDiscriminator(), + isa(closure), closure->getParent(), + capturedEnvs); } void ASTMangler::appendClosureComponents(Type Ty, unsigned discriminator, bool isImplicit, - const DeclContext *parentContext) { + const DeclContext *parentContext, + ArrayRef capturedEnvs) { assert(discriminator != AbstractClosureExpr::InvalidDiscriminator && "closure must be marked correctly with discriminator"); BaseEntitySignature base(parentContext->getInnermostDeclarationDeclContext()); appendContext(parentContext, base, StringRef()); - if (!Ty) - Ty = ErrorType::get(parentContext->getASTContext()); - auto Sig = parentContext->getGenericSignatureOfContext(); - Ty = Ty->mapTypeOutOfContext(); + + Ty = Ty.subst(MapLocalArchetypesOutOfContext(Sig, capturedEnvs), + MakeAbstractConformanceForGenericType(), + SubstFlags::PreservePackExpansionLevel); + appendType(Ty->getCanonicalType(), Sig); appendOperator(isImplicit ? "fu" : "fU", Index(discriminator)); }