From 80551ce8ad4dff0cb8048a5cc70705ad5d46bfae Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 23 Mar 2023 12:06:38 -0400 Subject: [PATCH 1/8] [NFC] Thread a common type through all the AST substitution code. There are a lot of problems caused by our highly-abstract substitution subsystem. Most of them would be solved by a more semantic / holistic understanding of the active transformation, but that's difficult to do because we just pass around function_refs. The first step in fixing that is to pass around a better currency type. For now, it can just hold the function_refs (and the SubstOptions). I've set it up so that the places that just apply SubstitutionMaps are constructing the IFS in a standard way; that should make it easy to change those places in the future. --- include/swift/AST/InFlightSubstitution.h | 88 +++++++++++++++++ include/swift/AST/PackConformance.h | 7 ++ include/swift/AST/ProtocolConformance.h | 7 ++ include/swift/AST/ProtocolConformanceRef.h | 7 ++ include/swift/AST/SubstitutionMap.h | 15 +++ include/swift/AST/Type.h | 7 ++ lib/AST/PackConformance.cpp | 50 +++++----- lib/AST/ProtocolConformance.cpp | 30 +++--- lib/AST/ProtocolConformanceRef.cpp | 24 +++-- lib/AST/SubstitutionMap.cpp | 35 ++++--- lib/AST/Type.cpp | 106 ++++++++++----------- 11 files changed, 258 insertions(+), 118 deletions(-) create mode 100644 include/swift/AST/InFlightSubstitution.h diff --git a/include/swift/AST/InFlightSubstitution.h b/include/swift/AST/InFlightSubstitution.h new file mode 100644 index 0000000000000..a137929d4c9d9 --- /dev/null +++ b/include/swift/AST/InFlightSubstitution.h @@ -0,0 +1,88 @@ +//===--- InFlightSubstitution.h - In-flight substitution data ---*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the InFlightSubstitution structure, which captures +// all the information about a type substitution that's currently in +// progress. For now, this is meant to be an internal implementation +// detail of the substitution system, and other systems should not use +// it (unless they are part of the extended substitution system, such as +// the SIL type substituter) +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_INFLIGHTSUBSTITUTION_H +#define SWIFT_AST_INFLIGHTSUBSTITUTION_H + +#include "swift/AST/SubstitutionMap.h" + +namespace swift { +class SubstitutionMap; + +class InFlightSubstitution { + SubstOptions Options; + TypeSubstitutionFn BaselineSubstType; + LookupConformanceFn BaselineLookupConformance; + +public: + InFlightSubstitution(TypeSubstitutionFn substType, + LookupConformanceFn lookupConformance, + SubstOptions options) + : Options(options), + BaselineSubstType(substType), + BaselineLookupConformance(lookupConformance) {} + + InFlightSubstitution(const InFlightSubstitution &) = delete; + InFlightSubstitution &operator=(const InFlightSubstitution &) = delete; + + Type substType(SubstitutableType *ty) { + return BaselineSubstType(ty); + } + + ProtocolConformanceRef lookupConformance(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) { + return BaselineLookupConformance(dependentType, + conformingReplacementType, + conformedProtocol); + } + + SubstOptions getOptions() const { + return Options; + } + + /// Is the given type invariant to substitution? + bool isInvariant(Type type) const; +}; + +/// A helper classes that provides stable storage for the query +/// functions against a SubstitutionMap. +struct InFlightSubstitutionViaSubMapHelper { + QuerySubstitutionMap QueryType; + LookUpConformanceInSubstitutionMap QueryConformance; + + InFlightSubstitutionViaSubMapHelper(SubstitutionMap subMap) + : QueryType{subMap}, QueryConformance(subMap) {} +}; +class InFlightSubstitutionViaSubMap : + private InFlightSubstitutionViaSubMapHelper, + public InFlightSubstitution { + +public: + InFlightSubstitutionViaSubMap(SubstitutionMap subMap, + SubstOptions options) + : InFlightSubstitutionViaSubMapHelper(subMap), + InFlightSubstitution(QueryType, QueryConformance, options) {} +}; + +} // end namespace swift + +#endif diff --git a/include/swift/AST/PackConformance.h b/include/swift/AST/PackConformance.h index eee457af99ff2..7c722932c3465 100644 --- a/include/swift/AST/PackConformance.h +++ b/include/swift/AST/PackConformance.h @@ -88,6 +88,13 @@ class alignas(1 << DeclAlignInBits) PackConformance final LookupConformanceFn conformances, SubstOptions options=None) const; + /// Apply an in-flight substitution to the conformances in this + /// protocol conformance ref. + /// + /// This function should generally not be used outside of the + /// substitution subsystem. + ProtocolConformanceRef subst(InFlightSubstitution &IFS) const; + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out, unsigned indent = 0) const; }; diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index dd24b843100b8..6e6348f439c8d 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -309,6 +309,13 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance LookupConformanceFn conformances, SubstOptions options=None) const; + /// Substitute the conforming type and produce a ProtocolConformance that + /// applies to the substituted type. + /// + /// This function should generally not be used outside of the substitution + /// subsystem. + ProtocolConformance *subst(InFlightSubstitution &IFS) const; + SWIFT_DEBUG_DUMP; void dump(llvm::raw_ostream &out, unsigned indent = 0) const; }; diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index 67ef66d9f9262..4f1fa2e244bc4 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -164,6 +164,13 @@ class ProtocolConformanceRef { LookupConformanceFn conformances, SubstOptions options=None) const; + /// Apply a substitution to the conforming type. + /// + /// This function should generally not be used outside of the substitution + /// subsystem. + ProtocolConformanceRef subst(Type origType, + InFlightSubstitution &IFS) const; + /// Map contextual types to interface types in the conformance. ProtocolConformanceRef mapConformanceOutOfContext() const; diff --git a/include/swift/AST/SubstitutionMap.h b/include/swift/AST/SubstitutionMap.h index 3cd722b316123..3c047451904ba 100644 --- a/include/swift/AST/SubstitutionMap.h +++ b/include/swift/AST/SubstitutionMap.h @@ -121,6 +121,14 @@ class SubstitutionMap { TypeSubstitutionFn subs, LookupConformanceFn lookupConformance); + /// Build a substitution map from the substitutions represented by + /// the given in-flight substitution. + /// + /// This function should generally only be used by the substitution + /// subsystem. + static SubstitutionMap get(GenericSignature genericSig, + InFlightSubstitution &IFS); + /// Retrieve the generic signature describing the environment in which /// substitutions occur. GenericSignature getGenericSignature() const; @@ -182,6 +190,13 @@ class SubstitutionMap { LookupConformanceFn conformances, SubstOptions options=None) const; + /// Apply an in-flight substitution to all replacement types in the map. + /// Does not change keys. + /// + /// This should generally not be used outside of the substitution + /// subsystem. + SubstitutionMap subst(InFlightSubstitution &subs) const; + /// Apply type expansion lowering to all types in the substitution map. Opaque /// archetypes will be lowered to their underlying types if the type expansion /// context allows. diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 6d59c99e8b48e..7bd6eb586c843 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -43,6 +43,7 @@ class ClassDecl; class CanType; class EnumDecl; class GenericSignatureImpl; +class InFlightSubstitution; class ModuleDecl; class NominalTypeDecl; class GenericTypeDecl; @@ -343,6 +344,12 @@ class Type { LookupConformanceFn conformances, SubstOptions options=None) const; + /// Apply an in-flight substitution to this type. + /// + /// This should generally not be used outside of the substitution + /// subsystem. + Type subst(InFlightSubstitution &subs) const; + bool isPrivateStdlibType(bool treatNonBuiltinProtocolsAsPublic = true) const; SWIFT_DEBUG_DUMP; diff --git a/lib/AST/PackConformance.cpp b/lib/AST/PackConformance.cpp index dd634bc643639..e4006e6c8ae7c 100644 --- a/lib/AST/PackConformance.cpp +++ b/lib/AST/PackConformance.cpp @@ -18,6 +18,7 @@ #include "swift/AST/PackConformance.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/InFlightSubstitution.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" @@ -162,9 +163,8 @@ PackConformance *PackConformance::getAssociatedConformance( ProtocolConformanceRef PackConformance::subst(SubstitutionMap subMap, SubstOptions options) const { - return subst(QuerySubstitutionMap{subMap}, - LookUpConformanceInSubstitutionMap(subMap), - options); + InFlightSubstitutionViaSubMap IFS(subMap, options); + return subst(IFS); } // TODO: Move this elsewhere since it's generally useful @@ -206,14 +206,9 @@ namespace { template class PackExpander { protected: - TypeSubstitutionFn subs; - LookupConformanceFn conformances; - SubstOptions options; + InFlightSubstitution &IFS; - PackExpander(TypeSubstitutionFn subs, - LookupConformanceFn conformances, - SubstOptions options) - : subs(subs), conformances(conformances), options(options) {} + PackExpander(InFlightSubstitution &IFS) : IFS(IFS) {} ImplClass *asImpl() { return static_cast(this); @@ -233,7 +228,7 @@ class PackExpander { // the expanded count pack type. llvm::SmallDenseMap expandedPacks; for (auto origParamType : rootParameterPacks) { - auto substParamType = origParamType.subst(subs, conformances, options); + auto substParamType = origParamType.subst(IFS); if (auto expandedParamType = substParamType->template getAs()) { assert(arePackShapesEqual(expandedParamType, expandedCountType) && @@ -262,7 +257,7 @@ class PackExpander { } // Compute the substituted type using our parent substitutions. - auto substType = Type(type).subst(subs, conformances, options); + auto substType = Type(type).subst(IFS); // If the substituted type is a pack, project the jth element. if (isRootParameterPack(type)) { @@ -277,12 +272,13 @@ class PackExpander { return packType->getElementType(j); } - return subs(type); + return IFS.substType(type); }; auto projectedConformances = [&](CanType origType, Type substType, ProtocolDecl *proto) -> ProtocolConformanceRef { - auto substConformance = conformances(origType, substType, proto); + auto substConformance = + IFS.lookupConformance(origType, substType, proto); // If the substituted conformance is a pack, project the jth element. if (isRootedInParameterPack(origType)) { @@ -294,7 +290,7 @@ class PackExpander { auto origCountElement = expandedCountType->getElementType(j); auto substCountElement = origCountElement.subst( - projectedSubs, projectedConformances, options); + projectedSubs, projectedConformances, IFS.getOptions()); asImpl()->add(origCountElement, substCountElement, i); } @@ -304,7 +300,7 @@ class PackExpander { /// form a new pack expansion. void addUnexpandedExpansion(Type origPatternType, Type substCountType, unsigned i) { - auto substPatternType = origPatternType.subst(subs, conformances, options); + auto substPatternType = origPatternType.subst(IFS); auto substExpansion = PackExpansionType::get(substPatternType, substCountType); asImpl()->add(origPatternType, substExpansion, i); @@ -313,7 +309,7 @@ class PackExpander { /// Scalar elements of the original pack are substituted and added to the /// flattened pack. void addScalar(Type origElement, unsigned i) { - auto substElement = origElement.subst(subs, conformances, options); + auto substElement = origElement.subst(IFS); asImpl()->add(origElement, substElement, i); } @@ -323,7 +319,7 @@ class PackExpander { auto origPatternType = origExpansion->getPatternType(); auto origCountType = origExpansion->getCountType(); - auto substCountType = origCountType.subst(subs, conformances, options); + auto substCountType = origCountType.subst(IFS); // If the substituted count type is a pack, we're expanding the // original element. @@ -358,19 +354,16 @@ class PackConformanceExpander : public PackExpander { ArrayRef origConformances; - PackConformanceExpander(TypeSubstitutionFn subs, - LookupConformanceFn conformances, - SubstOptions options, + PackConformanceExpander(InFlightSubstitution &IFS, ArrayRef origConformances) - : PackExpander(subs, conformances, options), - origConformances(origConformances) {} + : PackExpander(IFS), origConformances(origConformances) {} void add(Type origType, Type substType, unsigned i) { substElements.push_back(substType); // FIXME: Pass down projection callbacks substConformances.push_back(origConformances[i].subst( - origType, subs, conformances, options)); + origType, IFS)); } }; @@ -379,8 +372,13 @@ class PackConformanceExpander : public PackExpander { ProtocolConformanceRef PackConformance::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options) const { - PackConformanceExpander expander(subs, conformances, options, - getPatternConformances()); + InFlightSubstitution IFS(subs, conformances, options); + return subst(IFS); +} + +ProtocolConformanceRef +PackConformance::subst(InFlightSubstitution &IFS) const { + PackConformanceExpander expander(IFS, getPatternConformances()); expander.expand(ConformingType); auto &ctx = Protocol->getASTContext(); diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index b8a05c97da57a..bdbbbe1dcbd0c 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -22,6 +22,7 @@ #include "swift/AST/DistributedDecl.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/InFlightSubstitution.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/TypeCheckRequests.h" @@ -920,15 +921,20 @@ bool ProtocolConformance::isVisibleFrom(const DeclContext *dc) const { ProtocolConformance * ProtocolConformance::subst(SubstitutionMap subMap, SubstOptions options) const { - return subst(QuerySubstitutionMap{subMap}, - LookUpConformanceInSubstitutionMap(subMap), - options); + InFlightSubstitutionViaSubMap IFS(subMap, options); + return subst(IFS); } ProtocolConformance * ProtocolConformance::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options) const { + InFlightSubstitution IFS(subs, conformances, options); + return subst(IFS); +} + +ProtocolConformance * +ProtocolConformance::subst(InFlightSubstitution &IFS) const { switch (getKind()) { case ProtocolConformanceKind::Normal: { auto origType = getType(); @@ -936,12 +942,11 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, !origType->hasArchetype()) return const_cast(this); - auto substType = origType.subst(subs, conformances, options); + auto substType = origType.subst(IFS); if (substType->isEqual(origType)) return const_cast(this); - auto subMap = SubstitutionMap::get(getGenericSignature(), - subs, conformances); + auto subMap = SubstitutionMap::get(getGenericSignature(), IFS); auto *mutableThis = const_cast(this); return substType->getASTContext() @@ -955,7 +960,7 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, !origType->hasArchetype()) return const_cast(this); - auto substType = origType.subst(subs, conformances, options); + auto substType = origType.subst(IFS); // We do an exact pointer equality check because subst() can // change sugar. @@ -964,7 +969,7 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, SmallVector requirements; for (auto req : getConditionalRequirements()) { - requirements.push_back(req.subst(subs, conformances, options)); + requirements.push_back(req.subst(IFS)); } auto kind = cast(this) @@ -992,11 +997,10 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, if (origBaseType->hasTypeParameter() || origBaseType->hasArchetype()) { // Substitute into the superclass. - inheritedConformance = inheritedConformance->subst(subs, conformances, - options); + inheritedConformance = inheritedConformance->subst(IFS); } - auto substType = origType.subst(subs, conformances, options); + auto substType = origType.subst(IFS); return substType->getASTContext() .getInheritedConformance(substType, inheritedConformance); } @@ -1007,10 +1011,10 @@ ProtocolConformance::subst(TypeSubstitutionFn subs, auto subMap = spec->getSubstitutionMap(); auto origType = getType(); - auto substType = origType.subst(subs, conformances, options); + auto substType = origType.subst(IFS); return substType->getASTContext() .getSpecializedConformance(substType, genericConformance, - subMap.subst(subs, conformances, options)); + subMap.subst(IFS)); } } llvm_unreachable("bad ProtocolConformanceKind"); diff --git a/lib/AST/ProtocolConformanceRef.cpp b/lib/AST/ProtocolConformanceRef.cpp index 109bb40074621..e98bf08a22a21 100644 --- a/lib/AST/ProtocolConformanceRef.cpp +++ b/lib/AST/ProtocolConformanceRef.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Availability.h" #include "swift/AST/Decl.h" +#include "swift/AST/InFlightSubstitution.h" #include "swift/AST/Module.h" #include "swift/AST/PackConformance.h" #include "swift/AST/ProtocolConformance.h" @@ -57,10 +58,8 @@ ProtocolConformanceRef ProtocolConformanceRef::subst(Type origType, SubstitutionMap subMap, SubstOptions options) const { - return subst(origType, - QuerySubstitutionMap{subMap}, - LookUpConformanceInSubstitutionMap(subMap), - options); + InFlightSubstitutionViaSubMap IFS(subMap, options); + return subst(origType, IFS); } ProtocolConformanceRef @@ -68,28 +67,33 @@ ProtocolConformanceRef::subst(Type origType, TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options) const { + InFlightSubstitution IFS(subs, conformances, options); + return subst(origType, IFS); +} + +ProtocolConformanceRef +ProtocolConformanceRef::subst(Type origType, InFlightSubstitution &IFS) const { if (isInvalid()) return *this; if (isConcrete()) - return ProtocolConformanceRef(getConcrete()->subst(subs, conformances, - options)); + return ProtocolConformanceRef(getConcrete()->subst(IFS)); if (isPack()) - return getPack()->subst(subs, conformances, options); + return getPack()->subst(IFS); // Handle abstract conformances below: // If the type is an opaque archetype, the conformance will remain abstract, // unless we're specifically substituting opaque types. if (auto origArchetype = origType->getAs()) { - if (!options.contains(SubstFlags::SubstituteOpaqueArchetypes) + if (!IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) && isa(origArchetype)) { return *this; } } // Otherwise, compute the substituted type. - auto substType = origType.subst(subs, conformances, options); + auto substType = origType.subst(IFS); auto *proto = getRequirement(); @@ -105,7 +109,7 @@ ProtocolConformanceRef::subst(Type origType, } // Check the conformance map. - return conformances(origType->getCanonicalType(), substType, proto); + return IFS.lookupConformance(origType->getCanonicalType(), substType, proto); } ProtocolConformanceRef ProtocolConformanceRef::mapConformanceOutOfContext() const { diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index a490b51c8522f..d59f1961b5950 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -28,6 +28,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericParamList.h" +#include "swift/AST/InFlightSubstitution.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" @@ -209,6 +210,12 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, TypeSubstitutionFn subs, LookupConformanceFn lookupConformance) { + InFlightSubstitution IFS(subs, lookupConformance, None); + return get(genericSig, IFS); +} + +SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, + InFlightSubstitution &IFS) { if (!genericSig) { return SubstitutionMap(); } @@ -225,7 +232,7 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, } // Record the replacement. - Type replacement = Type(gp).subst(subs, lookupConformance); + Type replacement = Type(gp).subst(IFS); assert((!replacement || replacement->hasError() || gp->isParameterPack() == replacement->is()) && @@ -240,9 +247,9 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig, if (req.getKind() != RequirementKind::Conformance) continue; CanType depTy = req.getFirstType()->getCanonicalType(); - auto replacement = depTy.subst(subs, lookupConformance); + auto replacement = depTy.subst(IFS); auto *proto = req.getProtocolDecl(); - auto conformance = lookupConformance(depTy, replacement, proto); + auto conformance = IFS.lookupConformance(depTy, replacement, proto); conformances.push_back(conformance); } @@ -440,14 +447,18 @@ SubstitutionMap SubstitutionMap::mapReplacementTypesOutOfContext() const { SubstitutionMap SubstitutionMap::subst(SubstitutionMap subMap, SubstOptions options) const { - return subst(QuerySubstitutionMap{subMap}, - LookUpConformanceInSubstitutionMap(subMap), - options); + InFlightSubstitutionViaSubMap IFS(subMap, options); + return subst(IFS); } SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options) const { + InFlightSubstitution IFS(subs, conformances, options); + return subst(IFS); +} + +SubstitutionMap SubstitutionMap::subst(InFlightSubstitution &IFS) const { if (empty()) return SubstitutionMap(); SmallVector newSubs; @@ -457,7 +468,7 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, newSubs.push_back(Type()); continue; } - newSubs.push_back(type.subst(subs, conformances, options)); + newSubs.push_back(type.subst(IFS)); assert(type->is() == newSubs.back()->is() && "substitution changed the pack-ness of a replacement type"); } @@ -474,16 +485,14 @@ SubstitutionMap SubstitutionMap::subst(TypeSubstitutionFn subs, // Fast path for concrete case -- we don't need to compute substType // at all. if (conformance.isConcrete() && - !options.contains(SubstFlags::SubstituteOpaqueArchetypes)) { + !IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes)) { newConformances.push_back( - ProtocolConformanceRef( - conformance.getConcrete()->subst(subs, conformances, options))); + ProtocolConformanceRef(conformance.getConcrete()->subst(IFS))); } else { auto origType = req.getFirstType(); - auto substType = origType.subst(*this, options); + auto substType = origType.subst(*this, IFS.getOptions()); - newConformances.push_back( - conformance.subst(substType, subs, conformances, options)); + newConformances.push_back(conformance.subst(substType, IFS)); } oldConformances = oldConformances.slice(1); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 760191feb73bb..2c965368c254e 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -27,6 +27,7 @@ #include "swift/AST/TypeWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/InFlightSubstitution.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/PackConformance.h" @@ -4388,12 +4389,11 @@ CanGenericFunctionType::substGenericArgs(SubstitutionMap subs) const { getPointer()->substGenericArgs(subs)->getCanonicalType()); } -static Type getMemberForBaseType(LookupConformanceFn lookupConformances, +static Type getMemberForBaseType(InFlightSubstitution &IFS, Type origBase, Type substBase, AssociatedTypeDecl *assocType, - Identifier name, - SubstOptions options) { + Identifier name) { // Produce a dependent member type for the given base type. auto getDependentMemberType = [&](Type baseType) { if (assocType) @@ -4441,7 +4441,7 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, auto proto = assocType->getProtocol(); ProtocolConformanceRef conformance = - lookupConformances(origBase->getCanonicalType(), substBase, proto); + IFS.lookupConformance(origBase->getCanonicalType(), substBase, proto); if (conformance.isInvalid()) return failed(); @@ -4456,7 +4456,8 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, assocType->getDeclaredInterfaceType()); } else if (conformance.isConcrete()) { auto witness = - conformance.getConcrete()->getTypeWitnessAndDecl(assocType, options); + conformance.getConcrete()->getTypeWitnessAndDecl(assocType, + IFS.getOptions()); witnessTy = witness.getWitnessType(); if (!witnessTy || witnessTy->hasError()) @@ -4464,7 +4465,7 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances, // This is a hacky feature allowing code completion to migrate to // using Type::subst() without changing output. - if (options & SubstFlags::DesugarMemberTypes) { + if (IFS.getOptions() & SubstFlags::DesugarMemberTypes) { if (auto *aliasType = dyn_cast(witnessTy.getPointer())) witnessTy = aliasType->getSinglyDesugaredType(); @@ -4567,8 +4568,9 @@ Type DependentMemberType::substBaseType(Type substBase, substBase->hasTypeParameter()) return this; - return getMemberForBaseType(lookupConformance, getBase(), substBase, - getAssocType(), getName(), None); + InFlightSubstitution IFS(nullptr, lookupConformance, None); + return getMemberForBaseType(IFS, getBase(), substBase, + getAssocType(), getName()); } Type DependentMemberType::substRootParam(Type newRoot, @@ -4585,15 +4587,12 @@ Type DependentMemberType::substRootParam(Type newRoot, } static Type substGenericFunctionType(GenericFunctionType *genericFnType, - TypeSubstitutionFn substitutions, - LookupConformanceFn lookupConformances, - SubstOptions options) { + InFlightSubstitution &IFS) { // Substitute into the function type (without generic signature). auto *bareFnType = FunctionType::get(genericFnType->getParams(), genericFnType->getResult(), genericFnType->getExtInfo()); - Type result = - Type(bareFnType).subst(substitutions, lookupConformances, options); + Type result = Type(bareFnType).subst(IFS); if (!result || result->is()) return result; auto *fnType = result->castTo(); @@ -4601,8 +4600,7 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, bool anySemanticChanges = false; SmallVector genericParams; for (auto param : genericFnType->getGenericParams()) { - Type paramTy = - Type(param).subst(substitutions, lookupConformances, options); + Type paramTy = Type(param).subst(IFS); if (!paramTy) return Type(); @@ -4624,7 +4622,7 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, SmallVector requirements; for (const auto &req : genericFnType->getRequirements()) { // Substitute into the requirement. - auto substReqt = req.subst(substitutions, lookupConformances, options); + auto substReqt = req.subst(IFS); // Did anything change? if (!anySemanticChanges && @@ -4654,27 +4652,27 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, fnType->getResult(), fnType->getExtInfo()); } -static Type substType(Type derivedType, - TypeSubstitutionFn substitutions, - LookupConformanceFn lookupConformances, - SubstOptions options) { +bool InFlightSubstitution::isInvariant(Type derivedType) const { + return !derivedType->hasArchetype() + && !derivedType->hasTypeParameter() + && (!Options.contains(SubstFlags::SubstituteOpaqueArchetypes) + || !derivedType->hasOpaqueArchetype()); +} + +static Type substType(Type derivedType, InFlightSubstitution &IFS) { // Handle substitutions into generic function types. if (auto genericFnType = derivedType->getAs()) { - return substGenericFunctionType(genericFnType, substitutions, - lookupConformances, options); + return substGenericFunctionType(genericFnType, IFS); } // FIXME: Change getTypeOfMember() to not pass GenericFunctionType here - if (!derivedType->hasArchetype() - && !derivedType->hasTypeParameter() - && (!options.contains(SubstFlags::SubstituteOpaqueArchetypes) - || !derivedType->hasOpaqueArchetype())) + if (IFS.isInvariant(derivedType)) return derivedType; return derivedType.transformRec([&](TypeBase *type) -> Optional { // FIXME: Add SIL versions of mapTypeIntoContext() and // mapTypeOutOfContext() and use them appropriately - assert((options.contains(SubstFlags::AllowLoweredTypes) || + assert((IFS.getOptions().contains(SubstFlags::AllowLoweredTypes) || !isa(type)) && "should not be doing AST type-substitution on a lowered SIL type;" "use SILType::subst"); @@ -4683,7 +4681,7 @@ static Type substType(Type derivedType, // we want to structurally substitute the substitutions. if (auto boxTy = dyn_cast(type)) { auto subMap = boxTy->getSubstitutions(); - auto newSubMap = subMap.subst(substitutions, lookupConformances, options); + auto newSubMap = subMap.subst(IFS); return SILBoxType::get(boxTy->getASTContext(), boxTy->getLayout(), @@ -4691,10 +4689,8 @@ static Type substType(Type derivedType, } if (auto packExpansionTy = dyn_cast(type)) { - auto patternTy = substType(packExpansionTy->getPatternType(), - substitutions, lookupConformances, options); - auto countTy = substType(packExpansionTy->getCountType(), - substitutions, lookupConformances, options); + auto patternTy = substType(packExpansionTy->getPatternType(), IFS); + auto countTy = substType(packExpansionTy->getCountType(), IFS); if (auto *archetypeTy = countTy->getAs()) countTy = archetypeTy->getReducedShape(); @@ -4705,11 +4701,11 @@ static Type substType(Type derivedType, if (silFnTy->isPolymorphic()) return None; if (auto subs = silFnTy->getInvocationSubstitutions()) { - auto newSubs = subs.subst(substitutions, lookupConformances, options); + auto newSubs = subs.subst(IFS); return silFnTy->withInvocationSubstitutions(newSubs); } if (auto subs = silFnTy->getPatternSubstitutions()) { - auto newSubs = subs.subst(substitutions, lookupConformances, options); + auto newSubs = subs.subst(IFS); return silFnTy->withPatternSubstitutions(newSubs); } return None; @@ -4719,14 +4715,11 @@ static Type substType(Type derivedType, if (auto aliasTy = dyn_cast(type)) { Type parentTy; if (auto origParentTy = aliasTy->getParent()) - parentTy = substType(origParentTy, - substitutions, lookupConformances, options); - auto underlyingTy = substType(aliasTy->getSinglyDesugaredType(), - substitutions, lookupConformances, options); + parentTy = substType(origParentTy, IFS); + auto underlyingTy = substType(aliasTy->getSinglyDesugaredType(), IFS); if (parentTy && parentTy->isExistentialType()) return underlyingTy; - auto subMap = aliasTy->getSubstitutionMap() - .subst(substitutions, lookupConformances, options); + auto subMap = aliasTy->getSubstitutionMap().subst(IFS); return Type(TypeAliasType::get(aliasTy->getDecl(), parentTy, subMap, underlyingTy)); } @@ -4736,12 +4729,11 @@ static Type substType(Type derivedType, // For dependent member types, we may need to look up the member if the // base is resolved to a non-dependent type. if (auto depMemTy = dyn_cast(type)) { - auto newBase = substType(depMemTy->getBase(), - substitutions, lookupConformances, options); - return getMemberForBaseType(lookupConformances, + auto newBase = substType(depMemTy->getBase(), IFS); + return getMemberForBaseType(IFS, depMemTy->getBase(), newBase, depMemTy->getAssocType(), - depMemTy->getName(), options); + depMemTy->getName()); } auto substOrig = dyn_cast(type); @@ -4750,13 +4742,13 @@ static Type substType(Type derivedType, // Opaque types can't normally be directly substituted unless we // specifically were asked to substitute them. - if (!options.contains(SubstFlags::SubstituteOpaqueArchetypes) + if (!IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) && isa(substOrig)) return None; // If we have a substitution for this type, use it. - if (auto known = substitutions(substOrig)) { - if (options.contains(SubstFlags::SubstituteOpaqueArchetypes) && + if (auto known = IFS.substType(substOrig)) { + if (IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) && isa(substOrig) && known->getCanonicalType() == substOrig->getCanonicalType()) return None; // Recursively process the substitutions of the opaque type @@ -4784,8 +4776,7 @@ static Type substType(Type derivedType, assert(parent && "Not a nested archetype"); // Substitute into the parent type. - Type substParent = substType(parent, substitutions, - lookupConformances, options); + Type substParent = substType(parent, IFS); // If the parent didn't change, we won't change. if (substParent.getPointer() == parent) @@ -4795,23 +4786,26 @@ static Type substType(Type derivedType, AssociatedTypeDecl *assocType = origArchetype->getInterfaceType() ->castTo()->getAssocType(); - return getMemberForBaseType(lookupConformances, parent, substParent, - assocType, assocType->getName(), options); + return getMemberForBaseType(IFS, parent, substParent, + assocType, assocType->getName()); }); } Type Type::subst(SubstitutionMap substitutions, SubstOptions options) const { - return substType(*this, - QuerySubstitutionMap{substitutions}, - LookUpConformanceInSubstitutionMap(substitutions), - options); + InFlightSubstitutionViaSubMap IFS(substitutions, options); + return substType(*this, IFS); } Type Type::subst(TypeSubstitutionFn substitutions, LookupConformanceFn conformances, SubstOptions options) const { - return substType(*this, substitutions, conformances, options); + InFlightSubstitution IFS(substitutions, conformances, options); + return substType(*this, IFS); +} + +Type Type::subst(InFlightSubstitution &IFS) const { + return substType(*this, IFS); } DependentMemberType *TypeBase::findUnresolvedDependentMemberType() { From 06b5c972562fba089721b6d3c049ae7a5f1c4e51 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 23 Mar 2023 12:20:08 -0400 Subject: [PATCH 2/8] [NFC] Split the SIL type substitution code into its own file --- lib/SIL/IR/CMakeLists.txt | 1 + lib/SIL/IR/SILFunctionType.cpp | 708 ---------------------------- lib/SIL/IR/SILTypeSubstitution.cpp | 734 +++++++++++++++++++++++++++++ 3 files changed, 735 insertions(+), 708 deletions(-) create mode 100644 lib/SIL/IR/SILTypeSubstitution.cpp diff --git a/lib/SIL/IR/CMakeLists.txt b/lib/SIL/IR/CMakeLists.txt index 00b599bcbe050..336529098ab2f 100644 --- a/lib/SIL/IR/CMakeLists.txt +++ b/lib/SIL/IR/CMakeLists.txt @@ -30,6 +30,7 @@ target_sources(swiftSIL PRIVATE SILSuccessor.cpp SILSymbolVisitor.cpp SILType.cpp + SILTypeSubstitution.cpp SILUndef.cpp SILVTable.cpp SILValue.cpp diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 749b4cff0ed09..b9276b8204175 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -26,8 +26,6 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" -#include "swift/AST/PackConformance.h" -#include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/SIL/SILModule.h" @@ -4273,712 +4271,6 @@ TypeConverter::getConstantOverrideInfo(TypeExpansionContext context, return *result; } -namespace { - -/// Given a lowered SIL type, apply a substitution to it to produce another -/// lowered SIL type which uses the same abstraction conventions. -class SILTypeSubstituter : - public CanTypeVisitor { - TypeConverter &TC; - TypeSubstitutionFn Subst; - LookupConformanceFn Conformances; - // The signature for the original type. - // - // Replacement types are lowered with respect to the current - // context signature. - CanGenericSignature Sig; - - struct PackExpansion { - /// The shape class of pack parameters that are expanded by this - /// expansion. Set during construction and not changed. - CanType OrigShapeClass; - - /// The count type of the pack expansion in the current lane of - /// expansion, if any. Pack elements in this lane should be - /// expansions with this shape. - CanType SubstPackExpansionCount; - - /// The index of the current lane of expansion. Basic - /// substitution of pack parameters with the same shape as - /// OrigShapeClass should yield a pack, and lanewise - /// substitution should produce this element of that pack. - unsigned Index; - - PackExpansion(CanType origShapeClass) - : OrigShapeClass(origShapeClass), Index(0) {} - }; - SmallVector ActivePackExpansions; - - TypeExpansionContext typeExpansionContext; - - bool shouldSubstituteOpaqueArchetypes; - -public: - SILTypeSubstituter(TypeConverter &TC, - TypeExpansionContext context, - TypeSubstitutionFn Subst, - LookupConformanceFn Conformances, - CanGenericSignature Sig, - bool shouldSubstituteOpaqueArchetypes) - : TC(TC), - Subst(Subst), - Conformances(Conformances), - Sig(Sig), - typeExpansionContext(context), - shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) - {} - - // SIL type lowering only does special things to tuples and functions. - - // When a function appears inside of another type, we only perform - // substitutions if it is not polymorphic. - CanSILFunctionType visitSILFunctionType(CanSILFunctionType origType) { - return substSILFunctionType(origType, false); - } - - SubstitutionMap substOpaqueTypes(SubstitutionMap subs) { - if (!typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) - return subs; - - return subs.subst([&](SubstitutableType *s) -> Type { - return substOpaqueTypesWithUnderlyingTypes(s->getCanonicalType(), - typeExpansionContext); - }, [&](CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformedProtocol) -> ProtocolConformanceRef { - return substOpaqueTypesWithUnderlyingTypes( - ProtocolConformanceRef(conformedProtocol), - conformingReplacementType->getCanonicalType(), - typeExpansionContext); - }, SubstFlags::SubstituteOpaqueArchetypes); - } - - // Substitute a function type. - CanSILFunctionType substSILFunctionType(CanSILFunctionType origType, - bool isGenericApplication) { - assert((!isGenericApplication || origType->isPolymorphic()) && - "generic application without invocation signature or with " - "existing arguments"); - assert((!isGenericApplication || !shouldSubstituteOpaqueArchetypes) && - "generic application while substituting opaque archetypes"); - - // The general substitution rule is that we should only substitute - // into the free components of the type, i.e. the components that - // aren't inside a generic signature. That rule would say: - // - // - If there are invocation substitutions, just substitute those; - // the other components are necessarily inside the invocation - // generic signature. - // - // - Otherwise, if there's an invocation generic signature, - // substitute nothing. If we are applying generic arguments, - // add the appropriate invocation substitutions. - // - // - Otherwise, if there are pattern substitutions, just substitute - // those; the other components are inside the pattern generic - // signature. - // - // - Otherwise, substitute the basic components. - // - // There are two caveats here. The first is that we haven't yet - // written all the code that would be necessary in order to handle - // invocation substitutions everywhere, and so we never build those. - // Instead, we substitute into the pattern substitutions if present, - // or the components if not, and build a type with no invocation - // signature. As a special case, when substituting a coroutine type, - // we build pattern substitutions instead of substituting the - // component types in order to preserve the original yield structure, - // which factors into the continuation function ABI. - // - // The second is that this function is also used when substituting - // opaque archetypes. In this case, we may need to substitute - // into component types even within generic signatures. This is - // safe because the substitutions used in this case don't change - // generics, they just narrowly look through certain opaque archetypes. - // If substitutions are present, we still don't substitute into - // the basic components, in order to maintain the information about - // what was abstracted there. - - auto patternSubs = origType->getPatternSubstitutions(); - - // If we have an invocation signature, we generally shouldn't - // substitute into the pattern substitutions and component types. - if (auto sig = origType->getInvocationGenericSignature()) { - // Substitute the invocation substitutions if present. - if (auto invocationSubs = origType->getInvocationSubstitutions()) { - assert(!isGenericApplication); - invocationSubs = substSubstitutions(invocationSubs); - auto substType = - origType->withInvocationSubstitutions(invocationSubs); - - // Also do opaque-type substitutions on the pattern substitutions - // if requested and applicable. - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - substType = substType->withPatternSubstitutions(patternSubs); - } - - return substType; - } - - // Otherwise, we shouldn't substitute any components except - // when substituting opaque archetypes. - - // If we're doing a generic application, and there are pattern - // substitutions, substitute into the pattern substitutions; or if - // it's a coroutine, build pattern substitutions; or else, fall - // through to substitute the component types as discussed above. - if (isGenericApplication) { - if (patternSubs || origType->isCoroutine()) { - CanSILFunctionType substType = origType; - if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - substType = - origType->substituteOpaqueArchetypes(TC, typeExpansionContext); - } - - SubstitutionMap subs; - if (patternSubs) { - subs = substSubstitutions(patternSubs); - } else { - subs = SubstitutionMap::get(sig, Subst, Conformances); - } - auto witnessConformance = substWitnessConformance(origType); - substType = substType->withPatternSpecialization(nullptr, subs, - witnessConformance); - if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - substType = - substType->substituteOpaqueArchetypes(TC, typeExpansionContext); - } - return substType; - } - // else fall down to component substitution - - // If we're substituting opaque archetypes, and there are pattern - // substitutions present, just substitute those and preserve the - // basic structure in the component types. Otherwise, fall through - // to substitute the component types. - } else if (shouldSubstituteOpaqueArchetypes) { - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - return origType->withPatternSpecialization(sig, patternSubs, - witnessConformance); - } - // else fall down to component substitution - - // Otherwise, don't try to substitute bound components. - } else { - auto substType = origType; - if (patternSubs) { - patternSubs = substOpaqueTypes(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - substType = substType->withPatternSpecialization(sig, patternSubs, - witnessConformance); - } - return substType; - } - - // Otherwise, if there are pattern substitutions, just substitute - // into those and don't touch the component types. - } else if (patternSubs) { - patternSubs = substSubstitutions(patternSubs); - auto witnessConformance = substWitnessConformance(origType); - return origType->withPatternSpecialization(nullptr, patternSubs, - witnessConformance); - } - - // Otherwise, we need to substitute component types. - - SmallVector substResults; - substResults.reserve(origType->getNumResults()); - for (auto origResult : origType->getResults()) { - substResults.push_back(substInterface(origResult)); - } - - auto substErrorResult = origType->getOptionalErrorResult(); - assert(!substErrorResult || - (!substErrorResult->getInterfaceType()->hasTypeParameter() && - !substErrorResult->getInterfaceType()->hasArchetype())); - - SmallVector substParams; - substParams.reserve(origType->getParameters().size()); - for (auto &origParam : origType->getParameters()) { - substParams.push_back(substInterface(origParam)); - } - - SmallVector substYields; - substYields.reserve(origType->getYields().size()); - for (auto &origYield : origType->getYields()) { - substYields.push_back(substInterface(origYield)); - } - - auto witnessMethodConformance = substWitnessConformance(origType); - - // The substituted type is no longer generic, so it'd never be - // pseudogeneric. - auto extInfo = origType->getExtInfo(); - if (!shouldSubstituteOpaqueArchetypes) - extInfo = extInfo.intoBuilder().withIsPseudogeneric(false).build(); - - auto genericSig = shouldSubstituteOpaqueArchetypes - ? origType->getInvocationGenericSignature() - : nullptr; - - return SILFunctionType::get(genericSig, extInfo, - origType->getCoroutineKind(), - origType->getCalleeConvention(), substParams, - substYields, substResults, substErrorResult, - SubstitutionMap(), SubstitutionMap(), - TC.Context, witnessMethodConformance); - } - - ProtocolConformanceRef substWitnessConformance(CanSILFunctionType origType) { - auto conformance = origType->getWitnessMethodConformanceOrInvalid(); - if (!conformance) return conformance; - - assert(origType->getExtInfo().hasSelfParam()); - auto selfType = origType->getSelfParameter().getInterfaceType(); - - // The Self type can be nested in a few layers of metatypes (etc.). - while (auto metatypeType = dyn_cast(selfType)) { - auto next = metatypeType.getInstanceType(); - if (next == selfType) - break; - selfType = next; - } - - auto substConformance = - conformance.subst(selfType, Subst, Conformances); - - // Substitute the underlying conformance of opaque type archetypes if we - // should look through opaque archetypes. - if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - SubstOptions substOptions(None); - auto substType = selfType.subst(Subst, Conformances, substOptions) - ->getCanonicalType(); - if (substType->hasOpaqueArchetype()) { - substConformance = substOpaqueTypesWithUnderlyingTypes( - substConformance, substType, typeExpansionContext); - } - } - - return substConformance; - } - - SILType subst(SILType type) { - return SILType::getPrimitiveType(visit(type.getASTType()), - type.getCategory()); - } - - SILResultInfo substInterface(SILResultInfo orig) { - return SILResultInfo(visit(orig.getInterfaceType()), orig.getConvention()); - } - - SILYieldInfo substInterface(SILYieldInfo orig) { - return SILYieldInfo(visit(orig.getInterfaceType()), orig.getConvention()); - } - - SILParameterInfo substInterface(SILParameterInfo orig) { - return SILParameterInfo(visit(orig.getInterfaceType()), - orig.getConvention(), orig.getDifferentiability()); - } - - CanType visitSILPackType(CanSILPackType origType) { - // Fast-path the empty pack. - if (origType->getNumElements() == 0) return origType; - - SmallVector substEltTypes; - - substEltTypes.reserve(origType->getNumElements()); - - for (CanType origEltType : origType->getElementTypes()) { - if (auto origExpansionType = dyn_cast(origEltType)) { - substPackExpansion(origExpansionType, [&](CanType substExpandedType) { - substEltTypes.push_back(substExpandedType); - }); - } else { - auto substEltType = visit(origEltType); - substEltTypes.push_back(substEltType); - } - } - return SILPackType::get(TC.Context, origType->getExtInfo(), substEltTypes); - } - - CanType visitPackType(CanPackType origType) { - llvm_unreachable("CanPackType shouldn't show in lowered types"); - } - - CanType visitPackExpansionType(CanPackExpansionType origType) { - CanType patternType = visit(origType.getPatternType()); - CanType countType = substASTType(origType.getCountType()); - - return CanType(PackExpansionType::get(patternType, countType)); - } - - void substPackExpansion(CanPackExpansionType origType, - llvm::function_ref addExpandedType) { - CanType origCountType = origType.getCountType(); - CanType origPatternType = origType.getPatternType(); - - // Substitute the count type (as an AST type). - CanType substCountType = substASTType(origCountType); - - // If that produces a pack type, expand the pattern element-wise. - if (auto substCountPackType = dyn_cast(substCountType)) { - // Set up for element-wise expansion. - ActivePackExpansions.emplace_back(origCountType); - - for (CanType substCountEltType : substCountPackType.getElementTypes()) { - auto expansionType = dyn_cast(substCountEltType); - ActivePackExpansions.back().SubstPackExpansionCount = - (expansionType ? expansionType.getCountType() : CanType()); - - // Expand the pattern type in the element-wise context. - CanType expandedType = visit(origPatternType); - - // Turn that into a pack expansion if appropriate for the - // count element. - if (expansionType) { - expandedType = - CanPackExpansionType::get(expandedType, - expansionType.getCountType()); - } - - addExpandedType(expandedType); - - // Move to the next element. - ActivePackExpansions.back().Index++; - } - - // Leave the element-wise context. - ActivePackExpansions.pop_back(); - return; - } - - // Otherwise, transform the pattern type abstractly and just add a - // type expansion. - CanType substPatternType = visit(origPatternType); - - CanType expandedType; - if (substCountType == origCountType && substPatternType == origPatternType) - expandedType = origType; - else - expandedType = - CanPackExpansionType::get(substPatternType, substCountType); - addExpandedType(expandedType); - } - - /// Tuples need to have their component types substituted by these - /// same rules. - CanType visitTupleType(CanTupleType origType) { - // Fast-path the empty tuple. - if (origType->getNumElements() == 0) return origType; - - SmallVector substElts; - substElts.reserve(origType->getNumElements()); - for (auto &origElt : origType->getElements()) { - CanType origEltType = CanType(origElt.getType()); - if (auto origExpansion = dyn_cast(origEltType)) { - bool first = true; - substPackExpansion(origExpansion, [&](CanType substEltType) { - auto substElt = origElt.getWithType(substEltType); - if (first) { - first = false; - } else { - substElt = substElt.getWithoutName(); - } - substElts.push_back(substElt); - }); - } else { - auto substEltType = visit(origEltType); - substElts.push_back(origElt.getWithType(substEltType)); - } - } - - // Turn unlabeled singleton scalar tuples into their underlying types. - // The AST type substituter doesn't actually implement this rule yet, - // but we need to implement it in SIL in order to support testing, - // since the type parser can't parse a singleton tuple. - // - // For compatibility with previous behavior, don't do this if the - // original tuple type was singleton. AutoDiff apparently really - // likes making singleton tuples. - if (isParenType(substElts) && !isParenType(origType->getElements())) - return CanType(substElts[0].getType()); - - return CanType(TupleType::get(substElts, TC.Context)); - } - - static bool isParenType(ArrayRef elts) { - return (elts.size() == 1 && - !elts[0].hasName() && - !isa(CanType(elts[0].getType()))); - } - - // Block storage types need to substitute their capture type by these same - // rules. - CanType visitSILBlockStorageType(CanSILBlockStorageType origType) { - auto substCaptureType = visit(origType->getCaptureType()); - return SILBlockStorageType::get(substCaptureType); - } - - /// Optionals need to have their object types substituted by these rules. - CanType visitBoundGenericEnumType(CanBoundGenericEnumType origType) { - // Only use a special rule if it's Optional. - if (!origType->getDecl()->isOptionalDecl()) { - return visitType(origType); - } - - CanType origObjectType = origType.getGenericArgs()[0]; - CanType substObjectType = visit(origObjectType); - return CanType(BoundGenericType::get(origType->getDecl(), Type(), - substObjectType)); - } - - /// Any other type would be a valid type in the AST. Just apply the - /// substitution on the AST level and then lower that. - CanType visitType(CanType origType) { - assert(!isa(origType)); - assert(!isa(origType) && !isa(origType)); - - CanType substType = substASTType(origType); - - // If the substitution didn't change anything, we know that the - // original type was a lowered type, so we're good. - if (origType == substType) { - return origType; - } - - // We've looked through all the top-level structure in the orig - // type that's affected by type lowering. If substitution has - // given us a type with top-level structure that's affected by - // type lowering, it must be because the orig type was a type - // variable of some sort, and we should lower using an opaque - // abstraction pattern. If substitution hasn't given us such a - // type, it doesn't matter what abstraction pattern we use, - // lowering will just come back with substType. So we can just - // use an opaque abstraction pattern here and not put any effort - // into computing a more "honest" abstraction pattern. - AbstractionPattern abstraction = AbstractionPattern::getOpaque(); - return TC.getLoweredRValueType(typeExpansionContext, abstraction, - substType); - } - - struct SubstRespectingExpansions { - SILTypeSubstituter *_this; - SubstRespectingExpansions(SILTypeSubstituter *_this) : _this(_this) {} - - Type operator()(SubstitutableType *origType) const { - auto substType = _this->Subst(origType); - if (!substType) return substType; - auto substPackType = dyn_cast(substType->getCanonicalType()); - if (!substPackType) return substType; - auto activeExpansion = _this->getActivePackExpansion(CanType(origType)); - if (!activeExpansion) return substType; - auto substEltType = - substPackType.getElementType(activeExpansion->Index); - auto substExpansion = dyn_cast(substEltType); - assert((bool) substExpansion == - (bool) activeExpansion->SubstPackExpansionCount); - if (substExpansion) { - assert(_this->hasSameShape(substExpansion.getCountType(), - activeExpansion->SubstPackExpansionCount)); - return substExpansion.getPatternType(); - } - return substEltType; - } - }; - - struct SubstConformanceRespectingExpansions { - SILTypeSubstituter *_this; - SubstConformanceRespectingExpansions(SILTypeSubstituter *_this) - : _this(_this) {} - - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformingProtocol) const { - auto conformance = _this->Conformances(dependentType, - conformingReplacementType, - conformingProtocol); - if (!conformance || !conformance.isPack()) return conformance; - auto activeExpansion = _this->getActivePackExpansion(dependentType); - if (!activeExpansion) return conformance; - auto pack = conformance.getPack(); - auto substEltConf = - pack->getPatternConformances()[activeExpansion->Index]; - // There isn't currently a ProtocolConformanceExpansion that - // we would need to look through here. - return substEltConf; - }; - }; - - CanType substASTType(CanType origType) { - SubstOptions substOptions(None); - if (shouldSubstituteOpaqueArchetypes) - substOptions = SubstFlags::SubstituteOpaqueArchetypes | - SubstFlags::AllowLoweredTypes; - - if (ActivePackExpansions.empty()) - return origType.subst(Subst, Conformances, substOptions) - ->getCanonicalType(); - - return origType.subst(SubstRespectingExpansions(this), - SubstConformanceRespectingExpansions(this), - substOptions)->getCanonicalType(); - } - - SubstitutionMap substSubstitutions(SubstitutionMap subs) { - // Substitute the substitutions. - SubstOptions options = None; - if (shouldSubstituteOpaqueArchetypes) - options |= SubstFlags::SubstituteOpaqueArchetypes; - - // Expand substituted type according to the expansion context. - SubstitutionMap newSubs; - - if (ActivePackExpansions.empty()) - newSubs = subs.subst(Subst, Conformances, options); - else - newSubs = subs.subst(SubstRespectingExpansions(this), - SubstConformanceRespectingExpansions(this), - options); - - // If we need to look through opaque types in this context, re-substitute - // according to the expansion context. - newSubs = substOpaqueTypes(newSubs); - - return newSubs; - } - - PackExpansion *getActivePackExpansion(CanType dependentType) { - // We push new expansions onto the end of this vector, and we - // want to honor the innermost expansion, so we have to traverse - // in it reverse. - for (auto &entry : reverse(ActivePackExpansions)) { - if (hasSameShape(dependentType, entry.OrigShapeClass)) - return &entry; - } - return nullptr; - } - - bool hasSameShape(CanType lhs, CanType rhs) { - if (lhs->isTypeParameter() && rhs->isTypeParameter()) { - assert(Sig); - return Sig->haveSameShape(lhs, rhs); - } - - auto lhsArchetype = cast(lhs); - auto rhsArchetype = cast(rhs); - return lhsArchetype->getReducedShape() == rhsArchetype->getReducedShape(); - } -}; - -} // end anonymous namespace - -SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, - LookupConformanceFn conformances, - CanGenericSignature genericSig, - bool shouldSubstituteOpaqueArchetypes) const { - if (!hasArchetype() && !hasTypeParameter() && - (!shouldSubstituteOpaqueArchetypes || - !getASTType()->hasOpaqueArchetype())) - return *this; - - SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, - conformances, genericSig, - shouldSubstituteOpaqueArchetypes); - return STST.subst(*this); -} - -SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, - LookupConformanceFn conformances, - CanGenericSignature genericSig, - bool shouldSubstituteOpaqueArchetypes) const { - return subst(M.Types, subs, conformances, genericSig, - shouldSubstituteOpaqueArchetypes); -} - -SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { - auto sig = subs.getGenericSignature(); - return subst(tc, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - sig.getCanonicalSignature()); -} -SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ - return subst(M.Types, subs); -} - -SILType SILType::subst(SILModule &M, SubstitutionMap subs, - TypeExpansionContext context) const { - if (!hasArchetype() && !hasTypeParameter() && - !getASTType()->hasOpaqueArchetype()) - return *this; - - // Pass the TypeSubstitutionFn and LookupConformanceFn as arguments so that - // the llvm::function_ref value's scope spans the STST.subst call since - // SILTypeSubstituter captures these functions. - auto result = [&](TypeSubstitutionFn subsFn, - LookupConformanceFn conformancesFn) -> SILType { - SILTypeSubstituter STST(M.Types, context, subsFn, conformancesFn, - subs.getGenericSignature().getCanonicalSignature(), - false); - return STST.subst(*this); - }(QuerySubstitutionMap{subs}, LookUpConformanceInSubstitutionMap(subs)); - return result; -} - -/// Apply a substitution to this polymorphic SILFunctionType so that -/// it has the form of the normal SILFunctionType for the substituted -/// type, except using the original conventions. -CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, - TypeExpansionContext context) { - if (!isPolymorphic()) { - return CanSILFunctionType(this); - } - - if (subs.empty()) { - return CanSILFunctionType(this); - } - - return substGenericArgs(silModule, - QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - context); -} - -CanSILFunctionType -SILFunctionType::substGenericArgs(SILModule &silModule, - TypeSubstitutionFn subs, - LookupConformanceFn conformances, - TypeExpansionContext context) { - if (!isPolymorphic()) return CanSILFunctionType(this); - SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ false); - return substituter.substSILFunctionType(CanSILFunctionType(this), true); -} - -CanSILFunctionType -SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, - TypeExpansionContext context) { - if (!hasOpaqueArchetype() || - !context.shouldLookThroughOpaqueTypeArchetypes()) - return CanSILFunctionType(this); - - ReplaceOpaqueTypesWithUnderlyingTypes replacer( - context.getContext(), context.getResilienceExpansion(), - context.isWholeModuleContext()); - - SILTypeSubstituter substituter(TC, context, replacer, replacer, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ true); - auto resTy = - substituter.substSILFunctionType(CanSILFunctionType(this), false); - - return resTy; -} - /// Fast path for bridging types in a function type without uncurrying. CanAnyFunctionType TypeConverter::getBridgedFunctionType( AbstractionPattern pattern, CanAnyFunctionType t, Bridgeability bridging, diff --git a/lib/SIL/IR/SILTypeSubstitution.cpp b/lib/SIL/IR/SILTypeSubstitution.cpp new file mode 100644 index 0000000000000..4a5743405282f --- /dev/null +++ b/lib/SIL/IR/SILTypeSubstitution.cpp @@ -0,0 +1,734 @@ +//===--- SILTypeSubstitution.cpp - Apply substitutions to SIL types -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the core operations that apply substitutions to +// the lowered types used for SIL values. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "libsil" + +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILType.h" +#include "swift/AST/InFlightSubstitution.h" +#include "swift/AST/PackConformance.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/CanTypeVisitor.h" + +using namespace swift; +using namespace Lowering; + +namespace { + +/// Given a lowered SIL type, apply a substitution to it to produce another +/// lowered SIL type which uses the same abstraction conventions. +class SILTypeSubstituter : + public CanTypeVisitor { + TypeConverter &TC; + TypeSubstitutionFn Subst; + LookupConformanceFn Conformances; + // The signature for the original type. + // + // Replacement types are lowered with respect to the current + // context signature. + CanGenericSignature Sig; + + struct PackExpansion { + /// The shape class of pack parameters that are expanded by this + /// expansion. Set during construction and not changed. + CanType OrigShapeClass; + + /// The count type of the pack expansion in the current lane of + /// expansion, if any. Pack elements in this lane should be + /// expansions with this shape. + CanType SubstPackExpansionCount; + + /// The index of the current lane of expansion. Basic + /// substitution of pack parameters with the same shape as + /// OrigShapeClass should yield a pack, and lanewise + /// substitution should produce this element of that pack. + unsigned Index; + + PackExpansion(CanType origShapeClass) + : OrigShapeClass(origShapeClass), Index(0) {} + }; + SmallVector ActivePackExpansions; + + TypeExpansionContext typeExpansionContext; + + bool shouldSubstituteOpaqueArchetypes; + +public: + SILTypeSubstituter(TypeConverter &TC, + TypeExpansionContext context, + TypeSubstitutionFn Subst, + LookupConformanceFn Conformances, + CanGenericSignature Sig, + bool shouldSubstituteOpaqueArchetypes) + : TC(TC), + Subst(Subst), + Conformances(Conformances), + Sig(Sig), + typeExpansionContext(context), + shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) + {} + + // SIL type lowering only does special things to tuples and functions. + + // When a function appears inside of another type, we only perform + // substitutions if it is not polymorphic. + CanSILFunctionType visitSILFunctionType(CanSILFunctionType origType) { + return substSILFunctionType(origType, false); + } + + SubstitutionMap substOpaqueTypes(SubstitutionMap subs) { + if (!typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) + return subs; + + return subs.subst([&](SubstitutableType *s) -> Type { + return substOpaqueTypesWithUnderlyingTypes(s->getCanonicalType(), + typeExpansionContext); + }, [&](CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) -> ProtocolConformanceRef { + return substOpaqueTypesWithUnderlyingTypes( + ProtocolConformanceRef(conformedProtocol), + conformingReplacementType->getCanonicalType(), + typeExpansionContext); + }, SubstFlags::SubstituteOpaqueArchetypes); + } + + // Substitute a function type. + CanSILFunctionType substSILFunctionType(CanSILFunctionType origType, + bool isGenericApplication) { + assert((!isGenericApplication || origType->isPolymorphic()) && + "generic application without invocation signature or with " + "existing arguments"); + assert((!isGenericApplication || !shouldSubstituteOpaqueArchetypes) && + "generic application while substituting opaque archetypes"); + + // The general substitution rule is that we should only substitute + // into the free components of the type, i.e. the components that + // aren't inside a generic signature. That rule would say: + // + // - If there are invocation substitutions, just substitute those; + // the other components are necessarily inside the invocation + // generic signature. + // + // - Otherwise, if there's an invocation generic signature, + // substitute nothing. If we are applying generic arguments, + // add the appropriate invocation substitutions. + // + // - Otherwise, if there are pattern substitutions, just substitute + // those; the other components are inside the pattern generic + // signature. + // + // - Otherwise, substitute the basic components. + // + // There are two caveats here. The first is that we haven't yet + // written all the code that would be necessary in order to handle + // invocation substitutions everywhere, and so we never build those. + // Instead, we substitute into the pattern substitutions if present, + // or the components if not, and build a type with no invocation + // signature. As a special case, when substituting a coroutine type, + // we build pattern substitutions instead of substituting the + // component types in order to preserve the original yield structure, + // which factors into the continuation function ABI. + // + // The second is that this function is also used when substituting + // opaque archetypes. In this case, we may need to substitute + // into component types even within generic signatures. This is + // safe because the substitutions used in this case don't change + // generics, they just narrowly look through certain opaque archetypes. + // If substitutions are present, we still don't substitute into + // the basic components, in order to maintain the information about + // what was abstracted there. + + auto patternSubs = origType->getPatternSubstitutions(); + + // If we have an invocation signature, we generally shouldn't + // substitute into the pattern substitutions and component types. + if (auto sig = origType->getInvocationGenericSignature()) { + // Substitute the invocation substitutions if present. + if (auto invocationSubs = origType->getInvocationSubstitutions()) { + assert(!isGenericApplication); + invocationSubs = substSubstitutions(invocationSubs); + auto substType = + origType->withInvocationSubstitutions(invocationSubs); + + // Also do opaque-type substitutions on the pattern substitutions + // if requested and applicable. + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + substType = substType->withPatternSubstitutions(patternSubs); + } + + return substType; + } + + // Otherwise, we shouldn't substitute any components except + // when substituting opaque archetypes. + + // If we're doing a generic application, and there are pattern + // substitutions, substitute into the pattern substitutions; or if + // it's a coroutine, build pattern substitutions; or else, fall + // through to substitute the component types as discussed above. + if (isGenericApplication) { + if (patternSubs || origType->isCoroutine()) { + CanSILFunctionType substType = origType; + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + substType = + origType->substituteOpaqueArchetypes(TC, typeExpansionContext); + } + + SubstitutionMap subs; + if (patternSubs) { + subs = substSubstitutions(patternSubs); + } else { + subs = SubstitutionMap::get(sig, Subst, Conformances); + } + auto witnessConformance = substWitnessConformance(origType); + substType = substType->withPatternSpecialization(nullptr, subs, + witnessConformance); + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + substType = + substType->substituteOpaqueArchetypes(TC, typeExpansionContext); + } + return substType; + } + // else fall down to component substitution + + // If we're substituting opaque archetypes, and there are pattern + // substitutions present, just substitute those and preserve the + // basic structure in the component types. Otherwise, fall through + // to substitute the component types. + } else if (shouldSubstituteOpaqueArchetypes) { + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + return origType->withPatternSpecialization(sig, patternSubs, + witnessConformance); + } + // else fall down to component substitution + + // Otherwise, don't try to substitute bound components. + } else { + auto substType = origType; + if (patternSubs) { + patternSubs = substOpaqueTypes(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + substType = substType->withPatternSpecialization(sig, patternSubs, + witnessConformance); + } + return substType; + } + + // Otherwise, if there are pattern substitutions, just substitute + // into those and don't touch the component types. + } else if (patternSubs) { + patternSubs = substSubstitutions(patternSubs); + auto witnessConformance = substWitnessConformance(origType); + return origType->withPatternSpecialization(nullptr, patternSubs, + witnessConformance); + } + + // Otherwise, we need to substitute component types. + + SmallVector substResults; + substResults.reserve(origType->getNumResults()); + for (auto origResult : origType->getResults()) { + substResults.push_back(substInterface(origResult)); + } + + auto substErrorResult = origType->getOptionalErrorResult(); + assert(!substErrorResult || + (!substErrorResult->getInterfaceType()->hasTypeParameter() && + !substErrorResult->getInterfaceType()->hasArchetype())); + + SmallVector substParams; + substParams.reserve(origType->getParameters().size()); + for (auto &origParam : origType->getParameters()) { + substParams.push_back(substInterface(origParam)); + } + + SmallVector substYields; + substYields.reserve(origType->getYields().size()); + for (auto &origYield : origType->getYields()) { + substYields.push_back(substInterface(origYield)); + } + + auto witnessMethodConformance = substWitnessConformance(origType); + + // The substituted type is no longer generic, so it'd never be + // pseudogeneric. + auto extInfo = origType->getExtInfo(); + if (!shouldSubstituteOpaqueArchetypes) + extInfo = extInfo.intoBuilder().withIsPseudogeneric(false).build(); + + auto genericSig = shouldSubstituteOpaqueArchetypes + ? origType->getInvocationGenericSignature() + : nullptr; + + return SILFunctionType::get(genericSig, extInfo, + origType->getCoroutineKind(), + origType->getCalleeConvention(), substParams, + substYields, substResults, substErrorResult, + SubstitutionMap(), SubstitutionMap(), + TC.Context, witnessMethodConformance); + } + + ProtocolConformanceRef substWitnessConformance(CanSILFunctionType origType) { + auto conformance = origType->getWitnessMethodConformanceOrInvalid(); + if (!conformance) return conformance; + + assert(origType->getExtInfo().hasSelfParam()); + auto selfType = origType->getSelfParameter().getInterfaceType(); + + // The Self type can be nested in a few layers of metatypes (etc.). + while (auto metatypeType = dyn_cast(selfType)) { + auto next = metatypeType.getInstanceType(); + if (next == selfType) + break; + selfType = next; + } + + auto substConformance = + conformance.subst(selfType, Subst, Conformances); + + // Substitute the underlying conformance of opaque type archetypes if we + // should look through opaque archetypes. + if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { + SubstOptions substOptions(None); + auto substType = selfType.subst(Subst, Conformances, substOptions) + ->getCanonicalType(); + if (substType->hasOpaqueArchetype()) { + substConformance = substOpaqueTypesWithUnderlyingTypes( + substConformance, substType, typeExpansionContext); + } + } + + return substConformance; + } + + SILType subst(SILType type) { + return SILType::getPrimitiveType(visit(type.getASTType()), + type.getCategory()); + } + + SILResultInfo substInterface(SILResultInfo orig) { + return SILResultInfo(visit(orig.getInterfaceType()), orig.getConvention()); + } + + SILYieldInfo substInterface(SILYieldInfo orig) { + return SILYieldInfo(visit(orig.getInterfaceType()), orig.getConvention()); + } + + SILParameterInfo substInterface(SILParameterInfo orig) { + return SILParameterInfo(visit(orig.getInterfaceType()), + orig.getConvention(), orig.getDifferentiability()); + } + + CanType visitSILPackType(CanSILPackType origType) { + // Fast-path the empty pack. + if (origType->getNumElements() == 0) return origType; + + SmallVector substEltTypes; + + substEltTypes.reserve(origType->getNumElements()); + + for (CanType origEltType : origType->getElementTypes()) { + if (auto origExpansionType = dyn_cast(origEltType)) { + substPackExpansion(origExpansionType, [&](CanType substExpandedType) { + substEltTypes.push_back(substExpandedType); + }); + } else { + auto substEltType = visit(origEltType); + substEltTypes.push_back(substEltType); + } + } + return SILPackType::get(TC.Context, origType->getExtInfo(), substEltTypes); + } + + CanType visitPackType(CanPackType origType) { + llvm_unreachable("CanPackType shouldn't show in lowered types"); + } + + CanType visitPackExpansionType(CanPackExpansionType origType) { + CanType patternType = visit(origType.getPatternType()); + CanType countType = substASTType(origType.getCountType()); + + return CanType(PackExpansionType::get(patternType, countType)); + } + + void substPackExpansion(CanPackExpansionType origType, + llvm::function_ref addExpandedType) { + CanType origCountType = origType.getCountType(); + CanType origPatternType = origType.getPatternType(); + + // Substitute the count type (as an AST type). + CanType substCountType = substASTType(origCountType); + + // If that produces a pack type, expand the pattern element-wise. + if (auto substCountPackType = dyn_cast(substCountType)) { + // Set up for element-wise expansion. + ActivePackExpansions.emplace_back(origCountType); + + for (CanType substCountEltType : substCountPackType.getElementTypes()) { + auto expansionType = dyn_cast(substCountEltType); + ActivePackExpansions.back().SubstPackExpansionCount = + (expansionType ? expansionType.getCountType() : CanType()); + + // Expand the pattern type in the element-wise context. + CanType expandedType = visit(origPatternType); + + // Turn that into a pack expansion if appropriate for the + // count element. + if (expansionType) { + expandedType = + CanPackExpansionType::get(expandedType, + expansionType.getCountType()); + } + + addExpandedType(expandedType); + + // Move to the next element. + ActivePackExpansions.back().Index++; + } + + // Leave the element-wise context. + ActivePackExpansions.pop_back(); + return; + } + + // Otherwise, transform the pattern type abstractly and just add a + // type expansion. + CanType substPatternType = visit(origPatternType); + + CanType expandedType; + if (substCountType == origCountType && substPatternType == origPatternType) + expandedType = origType; + else + expandedType = + CanPackExpansionType::get(substPatternType, substCountType); + addExpandedType(expandedType); + } + + /// Tuples need to have their component types substituted by these + /// same rules. + CanType visitTupleType(CanTupleType origType) { + // Fast-path the empty tuple. + if (origType->getNumElements() == 0) return origType; + + SmallVector substElts; + substElts.reserve(origType->getNumElements()); + for (auto &origElt : origType->getElements()) { + CanType origEltType = CanType(origElt.getType()); + if (auto origExpansion = dyn_cast(origEltType)) { + bool first = true; + substPackExpansion(origExpansion, [&](CanType substEltType) { + auto substElt = origElt.getWithType(substEltType); + if (first) { + first = false; + } else { + substElt = substElt.getWithoutName(); + } + substElts.push_back(substElt); + }); + } else { + auto substEltType = visit(origEltType); + substElts.push_back(origElt.getWithType(substEltType)); + } + } + + // Turn unlabeled singleton scalar tuples into their underlying types. + // The AST type substituter doesn't actually implement this rule yet, + // but we need to implement it in SIL in order to support testing, + // since the type parser can't parse a singleton tuple. + // + // For compatibility with previous behavior, don't do this if the + // original tuple type was singleton. AutoDiff apparently really + // likes making singleton tuples. + if (isParenType(substElts) && !isParenType(origType->getElements())) + return CanType(substElts[0].getType()); + + return CanType(TupleType::get(substElts, TC.Context)); + } + + static bool isParenType(ArrayRef elts) { + return (elts.size() == 1 && + !elts[0].hasName() && + !isa(CanType(elts[0].getType()))); + } + + // Block storage types need to substitute their capture type by these same + // rules. + CanType visitSILBlockStorageType(CanSILBlockStorageType origType) { + auto substCaptureType = visit(origType->getCaptureType()); + return SILBlockStorageType::get(substCaptureType); + } + + /// Optionals need to have their object types substituted by these rules. + CanType visitBoundGenericEnumType(CanBoundGenericEnumType origType) { + // Only use a special rule if it's Optional. + if (!origType->getDecl()->isOptionalDecl()) { + return visitType(origType); + } + + CanType origObjectType = origType.getGenericArgs()[0]; + CanType substObjectType = visit(origObjectType); + return CanType(BoundGenericType::get(origType->getDecl(), Type(), + substObjectType)); + } + + /// Any other type would be a valid type in the AST. Just apply the + /// substitution on the AST level and then lower that. + CanType visitType(CanType origType) { + assert(!isa(origType)); + assert(!isa(origType) && !isa(origType)); + + CanType substType = substASTType(origType); + + // If the substitution didn't change anything, we know that the + // original type was a lowered type, so we're good. + if (origType == substType) { + return origType; + } + + // We've looked through all the top-level structure in the orig + // type that's affected by type lowering. If substitution has + // given us a type with top-level structure that's affected by + // type lowering, it must be because the orig type was a type + // variable of some sort, and we should lower using an opaque + // abstraction pattern. If substitution hasn't given us such a + // type, it doesn't matter what abstraction pattern we use, + // lowering will just come back with substType. So we can just + // use an opaque abstraction pattern here and not put any effort + // into computing a more "honest" abstraction pattern. + AbstractionPattern abstraction = AbstractionPattern::getOpaque(); + return TC.getLoweredRValueType(typeExpansionContext, abstraction, + substType); + } + + struct SubstRespectingExpansions { + SILTypeSubstituter *_this; + SubstRespectingExpansions(SILTypeSubstituter *_this) : _this(_this) {} + + Type operator()(SubstitutableType *origType) const { + auto substType = _this->Subst(origType); + if (!substType) return substType; + auto substPackType = dyn_cast(substType->getCanonicalType()); + if (!substPackType) return substType; + auto activeExpansion = _this->getActivePackExpansion(CanType(origType)); + if (!activeExpansion) return substType; + auto substEltType = + substPackType.getElementType(activeExpansion->Index); + auto substExpansion = dyn_cast(substEltType); + assert((bool) substExpansion == + (bool) activeExpansion->SubstPackExpansionCount); + if (substExpansion) { + assert(_this->hasSameShape(substExpansion.getCountType(), + activeExpansion->SubstPackExpansionCount)); + return substExpansion.getPatternType(); + } + return substEltType; + } + }; + + struct SubstConformanceRespectingExpansions { + SILTypeSubstituter *_this; + SubstConformanceRespectingExpansions(SILTypeSubstituter *_this) + : _this(_this) {} + + ProtocolConformanceRef operator()(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformingProtocol) const { + auto conformance = _this->Conformances(dependentType, + conformingReplacementType, + conformingProtocol); + if (!conformance || !conformance.isPack()) return conformance; + auto activeExpansion = _this->getActivePackExpansion(dependentType); + if (!activeExpansion) return conformance; + auto pack = conformance.getPack(); + auto substEltConf = + pack->getPatternConformances()[activeExpansion->Index]; + // There isn't currently a ProtocolConformanceExpansion that + // we would need to look through here. + return substEltConf; + }; + }; + + CanType substASTType(CanType origType) { + SubstOptions substOptions(None); + if (shouldSubstituteOpaqueArchetypes) + substOptions = SubstFlags::SubstituteOpaqueArchetypes | + SubstFlags::AllowLoweredTypes; + + if (ActivePackExpansions.empty()) + return origType.subst(Subst, Conformances, substOptions) + ->getCanonicalType(); + + return origType.subst(SubstRespectingExpansions(this), + SubstConformanceRespectingExpansions(this), + substOptions)->getCanonicalType(); + } + + SubstitutionMap substSubstitutions(SubstitutionMap subs) { + // Substitute the substitutions. + SubstOptions options = None; + if (shouldSubstituteOpaqueArchetypes) + options |= SubstFlags::SubstituteOpaqueArchetypes; + + // Expand substituted type according to the expansion context. + SubstitutionMap newSubs; + + if (ActivePackExpansions.empty()) + newSubs = subs.subst(Subst, Conformances, options); + else + newSubs = subs.subst(SubstRespectingExpansions(this), + SubstConformanceRespectingExpansions(this), + options); + + // If we need to look through opaque types in this context, re-substitute + // according to the expansion context. + newSubs = substOpaqueTypes(newSubs); + + return newSubs; + } + + PackExpansion *getActivePackExpansion(CanType dependentType) { + // We push new expansions onto the end of this vector, and we + // want to honor the innermost expansion, so we have to traverse + // in it reverse. + for (auto &entry : reverse(ActivePackExpansions)) { + if (hasSameShape(dependentType, entry.OrigShapeClass)) + return &entry; + } + return nullptr; + } + + bool hasSameShape(CanType lhs, CanType rhs) { + if (lhs->isTypeParameter() && rhs->isTypeParameter()) { + assert(Sig); + return Sig->haveSameShape(lhs, rhs); + } + + auto lhsArchetype = cast(lhs); + auto rhsArchetype = cast(rhs); + return lhsArchetype->getReducedShape() == rhsArchetype->getReducedShape(); + } +}; + +} // end anonymous namespace + +SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, + LookupConformanceFn conformances, + CanGenericSignature genericSig, + bool shouldSubstituteOpaqueArchetypes) const { + if (!hasArchetype() && !hasTypeParameter() && + (!shouldSubstituteOpaqueArchetypes || + !getASTType()->hasOpaqueArchetype())) + return *this; + + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, + conformances, genericSig, + shouldSubstituteOpaqueArchetypes); + return STST.subst(*this); +} + +SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, + LookupConformanceFn conformances, + CanGenericSignature genericSig, + bool shouldSubstituteOpaqueArchetypes) const { + return subst(M.Types, subs, conformances, genericSig, + shouldSubstituteOpaqueArchetypes); +} + +SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { + auto sig = subs.getGenericSignature(); + return subst(tc, QuerySubstitutionMap{subs}, + LookUpConformanceInSubstitutionMap(subs), + sig.getCanonicalSignature()); +} +SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ + return subst(M.Types, subs); +} + +SILType SILType::subst(SILModule &M, SubstitutionMap subs, + TypeExpansionContext context) const { + if (!hasArchetype() && !hasTypeParameter() && + !getASTType()->hasOpaqueArchetype()) + return *this; + + // Pass the TypeSubstitutionFn and LookupConformanceFn as arguments so that + // the llvm::function_ref value's scope spans the STST.subst call since + // SILTypeSubstituter captures these functions. + auto result = [&](TypeSubstitutionFn subsFn, + LookupConformanceFn conformancesFn) -> SILType { + SILTypeSubstituter STST(M.Types, context, subsFn, conformancesFn, + subs.getGenericSignature().getCanonicalSignature(), + false); + return STST.subst(*this); + }(QuerySubstitutionMap{subs}, LookUpConformanceInSubstitutionMap(subs)); + return result; +} + +/// Apply a substitution to this polymorphic SILFunctionType so that +/// it has the form of the normal SILFunctionType for the substituted +/// type, except using the original conventions. +CanSILFunctionType +SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, + TypeExpansionContext context) { + if (!isPolymorphic()) { + return CanSILFunctionType(this); + } + + if (subs.empty()) { + return CanSILFunctionType(this); + } + + return substGenericArgs(silModule, + QuerySubstitutionMap{subs}, + LookUpConformanceInSubstitutionMap(subs), + context); +} + +CanSILFunctionType +SILFunctionType::substGenericArgs(SILModule &silModule, + TypeSubstitutionFn subs, + LookupConformanceFn conformances, + TypeExpansionContext context) { + if (!isPolymorphic()) return CanSILFunctionType(this); + SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, + getSubstGenericSignature(), + /*shouldSubstituteOpaqueTypes*/ false); + return substituter.substSILFunctionType(CanSILFunctionType(this), true); +} + +CanSILFunctionType +SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, + TypeExpansionContext context) { + if (!hasOpaqueArchetype() || + !context.shouldLookThroughOpaqueTypeArchetypes()) + return CanSILFunctionType(this); + + ReplaceOpaqueTypesWithUnderlyingTypes replacer( + context.getContext(), context.getResilienceExpansion(), + context.isWholeModuleContext()); + + SILTypeSubstituter substituter(TC, context, replacer, replacer, + getSubstGenericSignature(), + /*shouldSubstituteOpaqueTypes*/ true); + auto resTy = + substituter.substSILFunctionType(CanSILFunctionType(this), false); + + return resTy; +} From 4edfd79fc9e14692f1757206a1257443bba86345 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 23 Mar 2023 12:50:25 -0400 Subject: [PATCH 3/8] [NFC] Add a convenience function to IFS --- include/swift/AST/InFlightSubstitution.h | 4 ++++ lib/AST/ProtocolConformanceRef.cpp | 2 +- lib/AST/SubstitutionMap.cpp | 2 +- lib/AST/Type.cpp | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/InFlightSubstitution.h b/include/swift/AST/InFlightSubstitution.h index a137929d4c9d9..15302efc3f527 100644 --- a/include/swift/AST/InFlightSubstitution.h +++ b/include/swift/AST/InFlightSubstitution.h @@ -59,6 +59,10 @@ class InFlightSubstitution { return Options; } + bool shouldSubstituteOpaqueArchetypes() const { + return Options.contains(SubstFlags::SubstituteOpaqueArchetypes); + } + /// Is the given type invariant to substitution? bool isInvariant(Type type) const; }; diff --git a/lib/AST/ProtocolConformanceRef.cpp b/lib/AST/ProtocolConformanceRef.cpp index e98bf08a22a21..ccde9d8d81f45 100644 --- a/lib/AST/ProtocolConformanceRef.cpp +++ b/lib/AST/ProtocolConformanceRef.cpp @@ -86,7 +86,7 @@ ProtocolConformanceRef::subst(Type origType, InFlightSubstitution &IFS) const { // If the type is an opaque archetype, the conformance will remain abstract, // unless we're specifically substituting opaque types. if (auto origArchetype = origType->getAs()) { - if (!IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) + if (!IFS.shouldSubstituteOpaqueArchetypes() && isa(origArchetype)) { return *this; } diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index d59f1961b5950..3d0214d77fc36 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -485,7 +485,7 @@ SubstitutionMap SubstitutionMap::subst(InFlightSubstitution &IFS) const { // Fast path for concrete case -- we don't need to compute substType // at all. if (conformance.isConcrete() && - !IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes)) { + !IFS.shouldSubstituteOpaqueArchetypes()) { newConformances.push_back( ProtocolConformanceRef(conformance.getConcrete()->subst(IFS))); } else { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 2c965368c254e..796a416f136ae 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4655,7 +4655,7 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, bool InFlightSubstitution::isInvariant(Type derivedType) const { return !derivedType->hasArchetype() && !derivedType->hasTypeParameter() - && (!Options.contains(SubstFlags::SubstituteOpaqueArchetypes) + && (!shouldSubstituteOpaqueArchetypes() || !derivedType->hasOpaqueArchetype()); } @@ -4742,13 +4742,13 @@ static Type substType(Type derivedType, InFlightSubstitution &IFS) { // Opaque types can't normally be directly substituted unless we // specifically were asked to substitute them. - if (!IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) + if (!IFS.shouldSubstituteOpaqueArchetypes() && isa(substOrig)) return None; // If we have a substitution for this type, use it. if (auto known = IFS.substType(substOrig)) { - if (IFS.getOptions().contains(SubstFlags::SubstituteOpaqueArchetypes) && + if (IFS.shouldSubstituteOpaqueArchetypes() && isa(substOrig) && known->getCanonicalType() == substOrig->getCanonicalType()) return None; // Recursively process the substitutions of the opaque type From 1042955d5966472429ee21a466ec39dada492286 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 23 Mar 2023 13:42:49 -0400 Subject: [PATCH 4/8] [NFC] Add an operation to change the current options on NFC --- include/swift/AST/InFlightSubstitution.h | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/swift/AST/InFlightSubstitution.h b/include/swift/AST/InFlightSubstitution.h index 15302efc3f527..1317b1508cecb 100644 --- a/include/swift/AST/InFlightSubstitution.h +++ b/include/swift/AST/InFlightSubstitution.h @@ -55,6 +55,31 @@ class InFlightSubstitution { conformedProtocol); } + class OptionsAdjustmentScope { + InFlightSubstitution &IFS; + SubstOptions SavedOptions; + + public: + OptionsAdjustmentScope(InFlightSubstitution &IFS, SubstOptions newOptions) + : IFS(IFS), SavedOptions(IFS.Options) { + IFS.Options = newOptions; + } + + OptionsAdjustmentScope(const OptionsAdjustmentScope &) = delete; + OptionsAdjustmentScope &operator=(const OptionsAdjustmentScope &) = delete; + + ~OptionsAdjustmentScope() { + IFS.Options = SavedOptions; + } + }; + + template + auto withNewOptions(SubstOptions options, Fn &&fn) + -> decltype(std::forward(fn)()) { + OptionsAdjustmentScope scope(*this, options); + return std::forward(fn)(); + } + SubstOptions getOptions() const { return Options; } From 3b3e53eb7348c7639c48dd093f82e0ebfbd9442a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 23 Mar 2023 13:43:20 -0400 Subject: [PATCH 5/8] [NFC] Use InFlightSubstitution in the SIL type substituter --- include/swift/AST/Types.h | 3 + include/swift/SIL/SILType.h | 4 + lib/SIL/IR/SILTypeSubstitution.cpp | 154 +++++++++++++++-------------- 3 files changed, 87 insertions(+), 74 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index f60c0bc181adc..8130c06f51682 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5168,6 +5168,9 @@ class SILFunctionType final TypeSubstitutionFn subs, LookupConformanceFn conformances, TypeExpansionContext context); + CanSILFunctionType substGenericArgs(SILModule &silModule, + InFlightSubstitution &IFS, + TypeExpansionContext context); CanSILFunctionType substituteOpaqueArchetypes(Lowering::TypeConverter &TC, TypeExpansionContext context); diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index a0331c5ead282..358956b88aa73 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -651,6 +651,10 @@ class SILType { CanGenericSignature genericSig = CanGenericSignature(), bool shouldSubstituteOpaqueArchetypes = false) const; + SILType subst(Lowering::TypeConverter &tc, + InFlightSubstitution &IFS, + CanGenericSignature genericSig) const; + SILType subst(Lowering::TypeConverter &tc, SubstitutionMap subs) const; SILType subst(SILModule &M, SubstitutionMap subs) const; diff --git a/lib/SIL/IR/SILTypeSubstitution.cpp b/lib/SIL/IR/SILTypeSubstitution.cpp index 4a5743405282f..51e8d242f6174 100644 --- a/lib/SIL/IR/SILTypeSubstitution.cpp +++ b/lib/SIL/IR/SILTypeSubstitution.cpp @@ -34,8 +34,8 @@ namespace { class SILTypeSubstituter : public CanTypeVisitor { TypeConverter &TC; - TypeSubstitutionFn Subst; - LookupConformanceFn Conformances; + InFlightSubstitution &IFS; + // The signature for the original type. // // Replacement types are lowered with respect to the current @@ -65,21 +65,15 @@ class SILTypeSubstituter : TypeExpansionContext typeExpansionContext; - bool shouldSubstituteOpaqueArchetypes; - public: SILTypeSubstituter(TypeConverter &TC, TypeExpansionContext context, - TypeSubstitutionFn Subst, - LookupConformanceFn Conformances, - CanGenericSignature Sig, - bool shouldSubstituteOpaqueArchetypes) + InFlightSubstitution &IFS, + CanGenericSignature Sig) : TC(TC), - Subst(Subst), - Conformances(Conformances), + IFS(IFS), Sig(Sig), - typeExpansionContext(context), - shouldSubstituteOpaqueArchetypes(shouldSubstituteOpaqueArchetypes) + typeExpansionContext(context) {} // SIL type lowering only does special things to tuples and functions. @@ -113,7 +107,7 @@ class SILTypeSubstituter : assert((!isGenericApplication || origType->isPolymorphic()) && "generic application without invocation signature or with " "existing arguments"); - assert((!isGenericApplication || !shouldSubstituteOpaqueArchetypes) && + assert((!isGenericApplication || !IFS.shouldSubstituteOpaqueArchetypes()) && "generic application while substituting opaque archetypes"); // The general substitution rule is that we should only substitute @@ -194,7 +188,7 @@ class SILTypeSubstituter : if (patternSubs) { subs = substSubstitutions(patternSubs); } else { - subs = SubstitutionMap::get(sig, Subst, Conformances); + subs = SubstitutionMap::get(sig, IFS); } auto witnessConformance = substWitnessConformance(origType); substType = substType->withPatternSpecialization(nullptr, subs, @@ -211,7 +205,7 @@ class SILTypeSubstituter : // substitutions present, just substitute those and preserve the // basic structure in the component types. Otherwise, fall through // to substitute the component types. - } else if (shouldSubstituteOpaqueArchetypes) { + } else if (IFS.shouldSubstituteOpaqueArchetypes()) { if (patternSubs) { patternSubs = substOpaqueTypes(patternSubs); auto witnessConformance = substWitnessConformance(origType); @@ -271,10 +265,10 @@ class SILTypeSubstituter : // The substituted type is no longer generic, so it'd never be // pseudogeneric. auto extInfo = origType->getExtInfo(); - if (!shouldSubstituteOpaqueArchetypes) + if (!IFS.shouldSubstituteOpaqueArchetypes()) extInfo = extInfo.intoBuilder().withIsPseudogeneric(false).build(); - auto genericSig = shouldSubstituteOpaqueArchetypes + auto genericSig = IFS.shouldSubstituteOpaqueArchetypes() ? origType->getInvocationGenericSignature() : nullptr; @@ -301,15 +295,14 @@ class SILTypeSubstituter : selfType = next; } - auto substConformance = - conformance.subst(selfType, Subst, Conformances); + auto substConformance = conformance.subst(selfType, IFS); // Substitute the underlying conformance of opaque type archetypes if we // should look through opaque archetypes. if (typeExpansionContext.shouldLookThroughOpaqueTypeArchetypes()) { - SubstOptions substOptions(None); - auto substType = selfType.subst(Subst, Conformances, substOptions) - ->getCanonicalType(); + auto substType = IFS.withNewOptions(None, [&] { + return selfType.subst(IFS)->getCanonicalType(); + }); if (substType->hasOpaqueArchetype()) { substConformance = substOpaqueTypesWithUnderlyingTypes( substConformance, substType, typeExpansionContext); @@ -523,7 +516,7 @@ class SILTypeSubstituter : SubstRespectingExpansions(SILTypeSubstituter *_this) : _this(_this) {} Type operator()(SubstitutableType *origType) const { - auto substType = _this->Subst(origType); + auto substType = _this->IFS.substType(origType); if (!substType) return substType; auto substPackType = dyn_cast(substType->getCanonicalType()); if (!substPackType) return substType; @@ -551,9 +544,10 @@ class SILTypeSubstituter : ProtocolConformanceRef operator()(CanType dependentType, Type conformingReplacementType, ProtocolDecl *conformingProtocol) const { - auto conformance = _this->Conformances(dependentType, - conformingReplacementType, - conformingProtocol); + auto conformance = + _this->IFS.lookupConformance(dependentType, + conformingReplacementType, + conformingProtocol); if (!conformance || !conformance.isPack()) return conformance; auto activeExpansion = _this->getActivePackExpansion(dependentType); if (!activeExpansion) return conformance; @@ -567,35 +561,23 @@ class SILTypeSubstituter : }; CanType substASTType(CanType origType) { - SubstOptions substOptions(None); - if (shouldSubstituteOpaqueArchetypes) - substOptions = SubstFlags::SubstituteOpaqueArchetypes | - SubstFlags::AllowLoweredTypes; - if (ActivePackExpansions.empty()) - return origType.subst(Subst, Conformances, substOptions) - ->getCanonicalType(); + return origType.subst(IFS)->getCanonicalType(); return origType.subst(SubstRespectingExpansions(this), SubstConformanceRespectingExpansions(this), - substOptions)->getCanonicalType(); + IFS.getOptions())->getCanonicalType(); } SubstitutionMap substSubstitutions(SubstitutionMap subs) { - // Substitute the substitutions. - SubstOptions options = None; - if (shouldSubstituteOpaqueArchetypes) - options |= SubstFlags::SubstituteOpaqueArchetypes; - - // Expand substituted type according to the expansion context. SubstitutionMap newSubs; if (ActivePackExpansions.empty()) - newSubs = subs.subst(Subst, Conformances, options); + newSubs = subs.subst(IFS); else newSubs = subs.subst(SubstRespectingExpansions(this), SubstConformanceRespectingExpansions(this), - options); + IFS.getOptions()); // If we need to look through opaque types in this context, re-substitute // according to the expansion context. @@ -629,18 +611,39 @@ class SILTypeSubstituter : } // end anonymous namespace +static bool isSubstitutionInvariant(SILType ty, + bool shouldSubstituteOpaqueArchetypes) { + return (!ty.hasArchetype() && + !ty.hasTypeParameter() && + (!shouldSubstituteOpaqueArchetypes || + !ty.getRawASTType()->hasOpaqueArchetype())); +} + SILType SILType::subst(TypeConverter &tc, TypeSubstitutionFn subs, LookupConformanceFn conformances, CanGenericSignature genericSig, bool shouldSubstituteOpaqueArchetypes) const { - if (!hasArchetype() && !hasTypeParameter() && - (!shouldSubstituteOpaqueArchetypes || - !getASTType()->hasOpaqueArchetype())) + if (isSubstitutionInvariant(*this, shouldSubstituteOpaqueArchetypes)) return *this; - SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), subs, - conformances, genericSig, - shouldSubstituteOpaqueArchetypes); + auto substOptions = + (shouldSubstituteOpaqueArchetypes + ? SubstOptions(SubstFlags::SubstituteOpaqueArchetypes) + : SubstOptions(None)); + InFlightSubstitution IFS(subs, conformances, substOptions); + + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), IFS, + genericSig); + return STST.subst(*this); +} + +SILType SILType::subst(TypeConverter &tc, InFlightSubstitution &IFS, + CanGenericSignature genericSig) const { + if (isSubstitutionInvariant(*this, IFS.shouldSubstituteOpaqueArchetypes())) + return *this; + + SILTypeSubstituter STST(tc, TypeExpansionContext::minimal(), IFS, + genericSig); return STST.subst(*this); } @@ -654,9 +657,9 @@ SILType SILType::subst(SILModule &M, TypeSubstitutionFn subs, SILType SILType::subst(TypeConverter &tc, SubstitutionMap subs) const { auto sig = subs.getGenericSignature(); - return subst(tc, QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - sig.getCanonicalSignature()); + + InFlightSubstitutionViaSubMap IFS(subs, None); + return subst(tc, IFS, sig.getCanonicalSignature()); } SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ return subst(M.Types, subs); @@ -664,21 +667,14 @@ SILType SILType::subst(SILModule &M, SubstitutionMap subs) const{ SILType SILType::subst(SILModule &M, SubstitutionMap subs, TypeExpansionContext context) const { - if (!hasArchetype() && !hasTypeParameter() && - !getASTType()->hasOpaqueArchetype()) + if (isSubstitutionInvariant(*this, false)) return *this; - // Pass the TypeSubstitutionFn and LookupConformanceFn as arguments so that - // the llvm::function_ref value's scope spans the STST.subst call since - // SILTypeSubstituter captures these functions. - auto result = [&](TypeSubstitutionFn subsFn, - LookupConformanceFn conformancesFn) -> SILType { - SILTypeSubstituter STST(M.Types, context, subsFn, conformancesFn, - subs.getGenericSignature().getCanonicalSignature(), - false); - return STST.subst(*this); - }(QuerySubstitutionMap{subs}, LookUpConformanceInSubstitutionMap(subs)); - return result; + InFlightSubstitutionViaSubMap IFS(subs, None); + + SILTypeSubstituter STST(M.Types, context, IFS, + subs.getGenericSignature().getCanonicalSignature()); + return STST.subst(*this); } /// Apply a substitution to this polymorphic SILFunctionType so that @@ -695,10 +691,9 @@ SILFunctionType::substGenericArgs(SILModule &silModule, SubstitutionMap subs, return CanSILFunctionType(this); } - return substGenericArgs(silModule, - QuerySubstitutionMap{subs}, - LookUpConformanceInSubstitutionMap(subs), - context); + InFlightSubstitutionViaSubMap IFS(subs, None); + + return substGenericArgs(silModule, IFS, context); } CanSILFunctionType @@ -707,9 +702,19 @@ SILFunctionType::substGenericArgs(SILModule &silModule, LookupConformanceFn conformances, TypeExpansionContext context) { if (!isPolymorphic()) return CanSILFunctionType(this); - SILTypeSubstituter substituter(silModule.Types, context, subs, conformances, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ false); + + InFlightSubstitution IFS(subs, conformances, None); + return substGenericArgs(silModule, IFS, context); +} + +CanSILFunctionType +SILFunctionType::substGenericArgs(SILModule &silModule, + InFlightSubstitution &IFS, + TypeExpansionContext context) { + if (!isPolymorphic()) return CanSILFunctionType(this); + + SILTypeSubstituter substituter(silModule.Types, context, IFS, + getSubstGenericSignature()); return substituter.substSILFunctionType(CanSILFunctionType(this), true); } @@ -724,9 +729,10 @@ SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, context.getContext(), context.getResilienceExpansion(), context.isWholeModuleContext()); - SILTypeSubstituter substituter(TC, context, replacer, replacer, - getSubstGenericSignature(), - /*shouldSubstituteOpaqueTypes*/ true); + InFlightSubstitution IFS(replacer, replacer, + SubstFlags::SubstituteOpaqueArchetypes); + + SILTypeSubstituter substituter(TC, context, IFS, getSubstGenericSignature()); auto resTy = substituter.substSILFunctionType(CanSILFunctionType(this), false); From d16fed6e9a506583531b39a406a83893ba6909b4 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 25 Mar 2023 18:39:44 -0400 Subject: [PATCH 6/8] The canonical type of a pack expansion has a reduced shape. --- lib/AST/ASTContext.cpp | 13 ++++++++++++- lib/AST/Type.cpp | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 553f90228ebeb..176a1b2c1857e 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3272,8 +3272,19 @@ PackExpansionType *PackExpansionType::get(Type patternType, Type countType) { .PackExpansionTypes.FindNodeOrInsertPos(id, insertPos)) return expType; + // The canonical pack expansion type uses the canonical shape. + // For interface types, we'd need a signature to do this properly, + // but for archetypes we can do it directly. + bool countIsCanonical = countType->isCanonical(); + if (countIsCanonical) { + if (auto archetype = dyn_cast(countType.getPointer())) { + auto reducedShape = archetype->getReducedShape(); + countIsCanonical = (reducedShape.getPointer() == archetype); + } + } + const ASTContext *canCtx = - (patternType->isCanonical() && countType->isCanonical()) + (patternType->isCanonical() && countIsCanonical) ? &context : nullptr; PackExpansionType *expansionType = new (context, arena) PackExpansionType(patternType, countType, properties, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 796a416f136ae..a43c332ebcf28 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1673,6 +1673,8 @@ CanType TypeBase::computeCanonicalType() { auto *expansion = cast(this); auto patternType = expansion->getPatternType()->getCanonicalType(); auto countType = expansion->getCountType()->getCanonicalType(); + if (auto packArchetype = dyn_cast(countType)) + countType = packArchetype->getReducedShape(); Result = PackExpansionType::get(patternType, countType); break; } From c041d1061a91c6fb0ae545d1568fb3659f2f55d6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 25 Mar 2023 18:46:29 -0400 Subject: [PATCH 7/8] Perform component-wise substitution of pack expansions immediately. Substitution of a pack expansion type may now produce a pack type. We immediately expand that pack when transforming a tuple, a function parameter, or a pack. I had to duplicate the component-wise transformation logic in the simplifyType transform, which I'm not pleased about, but a little code duplication seemed a lot better than trying to unify the code in two very different places. I think we're very close to being able to assert that pack expansion shapes are either pack archetypes or pack parameters; unfortunately, the pack matchers intentionally produce expansions of packs, and I didn't want to add that to an already-large patch. --- include/swift/AST/InFlightSubstitution.h | 78 +++++++- lib/AST/ASTContext.cpp | 6 + lib/AST/PackConformance.cpp | 224 +++++------------------ lib/AST/ParameterPack.cpp | 6 +- lib/AST/Type.cpp | 173 +++++++++++++++-- lib/SIL/IR/SILTypeSubstitution.cpp | 170 ++--------------- lib/Sema/CSApply.cpp | 4 +- lib/Sema/ConstraintSystem.cpp | 106 +++++++++-- lib/Sema/TypeCheckType.cpp | 19 +- 9 files changed, 399 insertions(+), 387 deletions(-) diff --git a/include/swift/AST/InFlightSubstitution.h b/include/swift/AST/InFlightSubstitution.h index 1317b1508cecb..bda9dd5d56118 100644 --- a/include/swift/AST/InFlightSubstitution.h +++ b/include/swift/AST/InFlightSubstitution.h @@ -32,6 +32,12 @@ class InFlightSubstitution { TypeSubstitutionFn BaselineSubstType; LookupConformanceFn BaselineLookupConformance; + struct ActivePackExpansion { + bool isSubstExpansion = false; + unsigned expansionIndex = 0; + }; + SmallVector ActivePackExpansions; + public: InFlightSubstitution(TypeSubstitutionFn substType, LookupConformanceFn lookupConformance, @@ -43,16 +49,72 @@ class InFlightSubstitution { InFlightSubstitution(const InFlightSubstitution &) = delete; InFlightSubstitution &operator=(const InFlightSubstitution &) = delete; - Type substType(SubstitutableType *ty) { - return BaselineSubstType(ty); - } - + // TODO: when we add PackElementType, we should recognize it during + // substitution and either call different methods on this class or + // pass an extra argument for the pack-expansion depth D. We should + // be able to rely on that to mark a pack-element reference instead + // of checking whether the original type was a pack. Substitution + // should use the D'th entry from the end of ActivePackExpansions to + // guide the element substitution: + // - project the given index of the pack substitution + // - wrap it in a PackElementType if it's a subst expansion + // - the depth of that PackElementType is the number of subst + // expansions between the depth entry and the end of + // ActivePackExpansions + + /// Perform primitive substitution on the given type. Returns Type() + /// if the type should not be substituted as a whole. + Type substType(SubstitutableType *origType); + + /// Perform primitive conformance lookup on the given type. ProtocolConformanceRef lookupConformance(CanType dependentType, Type conformingReplacementType, - ProtocolDecl *conformedProtocol) { - return BaselineLookupConformance(dependentType, - conformingReplacementType, - conformedProtocol); + ProtocolDecl *conformedProtocol); + + /// Given the shape type of a pack expansion, invoke the given callback + /// for each expanded component of it. If the substituted component + /// is an expansion component, the desired shape of that expansion + /// is passed as the argument; otherwise, the argument is Type(). + /// In either case, an active expansion is entered on this IFS for + /// the duration of the call to handleComponent, and subsequent + /// pack-element type references will substitute to the corresponding + /// element of the substitution of the pack. + void expandPackExpansionShape(Type origShape, + llvm::function_ref handleComponent); + + /// Call the given function for each expanded component type of the + /// given pack expansion type. The function will be invoked with the + /// active expansion still active. + void expandPackExpansionType(PackExpansionType *origExpansionType, + llvm::function_ref handleComponentType) { + expandPackExpansionShape(origExpansionType->getCountType(), + [&](Type substComponentShape) { + auto origPatternType = origExpansionType->getPatternType(); + auto substEltType = origPatternType.subst(*this); + + auto substComponentType = + (substComponentShape + ? PackExpansionType::get(substEltType, substComponentShape) + : substEltType); + handleComponentType(substComponentType); + }); + } + + /// Return a list of component types that the pack expansion expands to. + SmallVector + expandPackExpansionType(PackExpansionType *origExpansionType) { + SmallVector substComponentTypes; + expandPackExpansionType(origExpansionType, substComponentTypes); + return substComponentTypes; + } + + /// Expand the list of component types that the pack expansion expands + /// to into the given array. + void expandPackExpansionType(PackExpansionType *origExpansionType, + SmallVectorImpl &substComponentTypes) { + expandPackExpansionType(origExpansionType, [&](Type substComponentType) { + substComponentTypes.push_back(substComponentType); + }); } class OptionsAdjustmentScope { diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 176a1b2c1857e..b8213f4c1253a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3257,6 +3257,12 @@ CanPackExpansionType::get(CanType patternType, CanType countType) { } PackExpansionType *PackExpansionType::get(Type patternType, Type countType) { + assert(!patternType->is()); + assert(!countType->is()); + // FIXME: stop doing this deliberately in PackExpansionMatcher + //assert(!patternType->is()); + //assert(!countType->is()); + auto properties = patternType->getRecursiveProperties(); properties |= countType->getRecursiveProperties(); diff --git a/lib/AST/PackConformance.cpp b/lib/AST/PackConformance.cpp index e4006e6c8ae7c..93662faea8eac 100644 --- a/lib/AST/PackConformance.cpp +++ b/lib/AST/PackConformance.cpp @@ -167,207 +167,65 @@ ProtocolConformanceRef PackConformance::subst(SubstitutionMap subMap, return subst(IFS); } -// TODO: Move this elsewhere since it's generally useful -static bool arePackShapesEqual(PackType *lhs, PackType *rhs) { - if (lhs->getNumElements() != rhs->getNumElements()) - return false; - - for (unsigned i = 0, e = lhs->getNumElements(); i < e; ++i) { - auto lhsElt = lhs->getElementType(i); - auto rhsElt = rhs->getElementType(i); - - if (lhsElt->is() != rhsElt->is()) - return false; - } - - return true; -} - -static bool isRootParameterPack(Type t) { - if (auto *paramTy = t->getAs()) { - return paramTy->isParameterPack(); - } else if (auto *archetypeTy = t->getAs()) { - return archetypeTy->isRoot(); - } - - return false; -} - -static bool isRootedInParameterPack(Type t) { - if (auto *archetypeTy = t->getAs()) { - return true; - } - - return t->getRootGenericParam()->isParameterPack(); -} - namespace { -template -class PackExpander { -protected: +struct PackConformanceExpander { InFlightSubstitution &IFS; + ArrayRef origConformances; - PackExpander(InFlightSubstitution &IFS) : IFS(IFS) {} - - ImplClass *asImpl() { - return static_cast(this); - } - - /// We're replacing a pack expansion type with a pack -- flatten the pack - /// using the pack expansion's pattern. - void addExpandedExpansion(Type origPatternType, PackType *expandedCountType, - unsigned i) { - - // Get all pack parameters referenced from the pattern. - SmallVector rootParameterPacks; - origPatternType->getTypeParameterPacks(rootParameterPacks); - - // Each pack parameter referenced from the pattern must be replaced - // with a pack type, and all pack types must have the same shape as - // the expanded count pack type. - llvm::SmallDenseMap expandedPacks; - for (auto origParamType : rootParameterPacks) { - auto substParamType = origParamType.subst(IFS); - - if (auto expandedParamType = substParamType->template getAs()) { - assert(arePackShapesEqual(expandedParamType, expandedCountType) && - "TODO: Return an invalid conformance if this fails"); - - auto inserted = expandedPacks.insert( - std::make_pair(origParamType->getCanonicalType(), - expandedParamType)).second; - assert(inserted && - "getTypeParameterPacks() should not return duplicates"); - } else { - assert(false && - "TODO: Return an invalid conformance if this fails"); - } - } - - // For each element of the expanded count, compute the substituted - // pattern type. - for (unsigned j = 0, ee = expandedCountType->getNumElements(); j < ee; ++j) { - auto projectedSubs = [&](SubstitutableType *type) -> Type { - // Nested sequence archetypes get passed in here, but we must - // handle them via the standard nested type path. - if (auto *archetypeType = dyn_cast(type)) { - if (!archetypeType->isRoot()) - return Type(); - } - - // Compute the substituted type using our parent substitutions. - auto substType = Type(type).subst(IFS); - - // If the substituted type is a pack, project the jth element. - if (isRootParameterPack(type)) { - // FIXME: What if you have something like G... where G<> is - // variadic? - assert(substType->template is() && - "TODO: Return an invalid conformance if this fails"); - auto *packType = substType->template castTo(); - assert(arePackShapesEqual(packType, expandedCountType) && - "TODO: Return an invalid conformance if this fails"); - - return packType->getElementType(j); - } - - return IFS.substType(type); - }; - - auto projectedConformances = [&](CanType origType, Type substType, - ProtocolDecl *proto) -> ProtocolConformanceRef { - auto substConformance = - IFS.lookupConformance(origType, substType, proto); - - // If the substituted conformance is a pack, project the jth element. - if (isRootedInParameterPack(origType)) { - return substConformance.getPack()->getPatternConformances()[j]; - } - - return substConformance; - }; - - auto origCountElement = expandedCountType->getElementType(j); - auto substCountElement = origCountElement.subst( - projectedSubs, projectedConformances, IFS.getOptions()); - - asImpl()->add(origCountElement, substCountElement, i); - } - } - - /// A pack expansion remains unexpanded, so we substitute the pattern and - /// form a new pack expansion. - void addUnexpandedExpansion(Type origPatternType, Type substCountType, - unsigned i) { - auto substPatternType = origPatternType.subst(IFS); - auto substExpansion = PackExpansionType::get(substPatternType, substCountType); +public: + // Results built up by the expansion. + SmallVector substElementTypes; + SmallVector substConformances; - asImpl()->add(origPatternType, substExpansion, i); - } + PackConformanceExpander(InFlightSubstitution &IFS, + ArrayRef origConformances) + : IFS(IFS), origConformances(origConformances) {} - /// Scalar elements of the original pack are substituted and added to the - /// flattened pack. - void addScalar(Type origElement, unsigned i) { - auto substElement = origElement.subst(IFS); +private: + /// Substitute a scalar element of the original pack. + void substScalar(Type origElementType, + ProtocolConformanceRef origConformance) { + auto substElementType = origElementType.subst(IFS); + auto substConformance = origConformance.subst(origElementType, IFS); - asImpl()->add(origElement, substElement, i); + substElementTypes.push_back(substElementType); + substConformances.push_back(substConformance); } - /// Potentially expand an element of the original pack. - void maybeExpandExpansion(PackExpansionType *origExpansion, unsigned i) { - auto origPatternType = origExpansion->getPatternType(); - auto origCountType = origExpansion->getCountType(); - - auto substCountType = origCountType.subst(IFS); - - // If the substituted count type is a pack, we're expanding the - // original element. - if (auto *expandedCountType = substCountType->template getAs()) { - addExpandedExpansion(origPatternType, expandedCountType, i); - return; - } - - addUnexpandedExpansion(origPatternType, substCountType, i); + /// Substitute and expand an expansion element of the original pack. + void substExpansion(PackExpansionType *origExpansionType, + ProtocolConformanceRef origConformance) { + IFS.expandPackExpansionType(origExpansionType, + [&](Type substComponentType) { + auto origPatternType = origExpansionType->getPatternType(); + + // Just substitute the conformance. We don't directly represent + // pack expansion conformances here; it's sort of implicit in the + // corresponding pack element type. + auto substConformance = origConformance.subst(origPatternType, IFS); + + substElementTypes.push_back(substComponentType); + substConformances.push_back(substConformance); + }); } public: void expand(PackType *origPackType) { - for (unsigned i = 0, e = origPackType->getNumElements(); i < e; ++i) { - auto origElement = origPackType->getElementType(i); + assert(origPackType->getNumElements() == origConformances.size()); - // Check if the original element is potentially being expanded. - if (auto *origExpansion = origElement->getAs()) { - maybeExpandExpansion(origExpansion, i); - continue; + for (auto i : range(origPackType->getNumElements())) { + auto origElementType = origPackType->getElementType(i); + if (auto *origExpansion = origElementType->getAs()) { + substExpansion(origExpansion, origConformances[i]); + } else { + substScalar(origElementType, origConformances[i]); } - - addScalar(origElement, i); } } }; -class PackConformanceExpander : public PackExpander { -public: - SmallVector substElements; - SmallVector substConformances; - - ArrayRef origConformances; - - PackConformanceExpander(InFlightSubstitution &IFS, - ArrayRef origConformances) - : PackExpander(IFS), origConformances(origConformances) {} - - void add(Type origType, Type substType, unsigned i) { - substElements.push_back(substType); - - // FIXME: Pass down projection callbacks - substConformances.push_back(origConformances[i].subst( - origType, IFS)); - } -}; - -} +} // end anonymous namespace ProtocolConformanceRef PackConformance::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, @@ -382,7 +240,7 @@ PackConformance::subst(InFlightSubstitution &IFS) const { expander.expand(ConformingType); auto &ctx = Protocol->getASTContext(); - auto *substConformingType = PackType::get(ctx, expander.substElements); + auto *substConformingType = PackType::get(ctx, expander.substElementTypes); auto substConformance = PackConformance::get(substConformingType, Protocol, expander.substConformances); diff --git a/lib/AST/ParameterPack.cpp b/lib/AST/ParameterPack.cpp index d3957e718d41a..fb2e93b80219d 100644 --- a/lib/AST/ParameterPack.cpp +++ b/lib/AST/ParameterPack.cpp @@ -449,15 +449,15 @@ PackType *PackType::get(const ASTContext &C, auto arg = args[i]; if (params[i]->isParameterPack()) { - wrappedArgs.push_back(PackExpansionType::get( - arg, arg->getReducedShape())); + auto argPackElements = arg->castTo()->getElementTypes(); + wrappedArgs.append(argPackElements.begin(), argPackElements.end()); continue; } wrappedArgs.push_back(arg); } - return get(C, wrappedArgs)->flattenPackTypes(); + return get(C, wrappedArgs); } PackType *PackType::getSingletonPackExpansion(Type param) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a43c332ebcf28..90d9612b333ea 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4654,6 +4654,84 @@ static Type substGenericFunctionType(GenericFunctionType *genericFnType, fnType->getResult(), fnType->getExtInfo()); } +void InFlightSubstitution::expandPackExpansionShape(Type origShape, + llvm::function_ref handleComponent) { + + // Substitute the shape using the baseline substitutions, not the + // current elementwise projections. + auto substShape = origShape.subst(BaselineSubstType, + BaselineLookupConformance, + Options); + + auto substPackShape = substShape->getAs(); + if (!substPackShape) { + ActivePackExpansions.push_back({/*is subst expansion*/true, 0}); + handleComponent(substShape); + ActivePackExpansions.pop_back(); + return; + } + + ActivePackExpansions.push_back({false, 0}); + for (auto substElt : substPackShape->getElementTypes()) { + auto substExpansion = substElt->getAs(); + auto substExpansionShape = + (substExpansion ? substExpansion->getCountType() : Type()); + + ActivePackExpansions.back().isSubstExpansion = + (substExpansion != nullptr); + handleComponent(substExpansionShape); + ActivePackExpansions.back().expansionIndex++; + } + ActivePackExpansions.pop_back(); +} + +Type InFlightSubstitution::substType(SubstitutableType *origType) { + auto substType = BaselineSubstType(origType); + if (!substType || ActivePackExpansions.empty()) + return substType; + + auto substPackType = substType->getAs(); + if (!substPackType) + return substType; + + auto &activeExpansion = ActivePackExpansions.back(); + auto index = activeExpansion.expansionIndex; + assert(index < substPackType->getNumElements() && + "replacement for pack parameter did not have the right " + "size for expansion"); + auto substEltType = substPackType->getElementType(index); + if (activeExpansion.isSubstExpansion) { + assert(substEltType->is() && + "substituted shape mismatch: expected an expansion component"); + substEltType = substEltType->castTo()->getPatternType(); + } else { + assert(!substEltType->is() && + "substituted shape mismatch: expected a scalar component"); + } + return substEltType; +} + +ProtocolConformanceRef +InFlightSubstitution::lookupConformance(CanType dependentType, + Type conformingReplacementType, + ProtocolDecl *conformedProtocol) { + auto substConfRef = BaselineLookupConformance(dependentType, + conformingReplacementType, + conformedProtocol); + if (!substConfRef || + ActivePackExpansions.empty() || + !substConfRef.isPack()) + return substConfRef; + + auto substPackConf = substConfRef.getPack(); + auto substPackPatterns = substPackConf->getPatternConformances(); + auto index = ActivePackExpansions.back().expansionIndex; + assert(index < substPackPatterns.size() && + "replacement for pack parameter did not have the right " + "size for expansion"); + return substPackPatterns[index]; +} + bool InFlightSubstitution::isInvariant(Type derivedType) const { return !derivedType->hasArchetype() && !derivedType->hasTypeParameter() @@ -4691,12 +4769,9 @@ static Type substType(Type derivedType, InFlightSubstitution &IFS) { } if (auto packExpansionTy = dyn_cast(type)) { - auto patternTy = substType(packExpansionTy->getPatternType(), IFS); - auto countTy = substType(packExpansionTy->getCountType(), IFS); - if (auto *archetypeTy = countTy->getAs()) - countTy = archetypeTy->getReducedShape(); - - return Type(PackExpansionType::get(patternTy, countTy)->expand()); + auto eltTys = IFS.expandPackExpansionType(packExpansionTy); + if (eltTys.size() == 1) return eltTys[0]; + return Type(PackType::get(packExpansionTy->getASTContext(), eltTys)); } if (auto silFnTy = dyn_cast(type)) { @@ -5194,6 +5269,23 @@ Type Type::transform(llvm::function_ref fn) const { }); } +static PackType *getTransformedPack(Type substType) { + if (auto pack = substType->getAs()) { + return pack; + } + + // The pack matchers like to make expansions out of packs, and + // these types then propagate out into transforms. Make sure we + // flatten them exactly if they were the underlying pack. + // FIXME: stop doing this and make PackExpansionType::get assert + // that we never construct these types + if (auto expansion = substType->getAs()) { + return expansion->getPatternType()->getAs(); + } + + return nullptr; +} + Type Type::transformRec( llvm::function_ref(TypeBase *)> fn) const { return transformWithPosition(TypePosition::Invariant, @@ -5631,13 +5723,19 @@ case TypeKind::Id: anyChanged = true; } - elements.push_back(transformedEltTy); + // If the transformed type is a pack, immediately expand it. + if (auto eltPack = getTransformedPack(transformedEltTy)) { + auto eltElements = eltPack->getElementTypes(); + elements.append(eltElements.begin(), eltElements.end()); + } else { + elements.push_back(transformedEltTy); + } } if (!anyChanged) return *this; - return PackType::get(Ptr->getASTContext(), elements)->flattenPackTypes(); + return PackType::get(Ptr->getASTContext(), elements); } case TypeKind::SILPack: { @@ -5689,6 +5787,8 @@ case TypeKind::Id: case TypeKind::PackExpansion: { auto expand = cast(base); + // Substitution completely replaces this. + Type transformedPat = expand->getPatternType().transformWithPosition(pos, fn); if (!transformedPat) @@ -5703,7 +5803,14 @@ case TypeKind::Id: transformedCount.getPointer() == expand->getCountType().getPointer()) return *this; - return PackExpansionType::get(transformedPat, transformedCount)->expand(); + // // If we transform the count to a pack type, expand the pattern. + // // This is necessary because of how we piece together types in + // // the constraint system. + // if (auto countPack = transformedCount->getAs()) { + // return PackExpansionType::expand(transformedPat, countPack); + // } + + return PackExpansionType::get(transformedPat, transformedCount); } case TypeKind::Tuple: { @@ -5734,13 +5841,35 @@ case TypeKind::Id: } // Add the new tuple element, with the transformed type. - elements.push_back(elt.getWithType(transformedEltTy)); + // Expand packs immediately. + if (auto eltPack = getTransformedPack(transformedEltTy)) { + bool first = true; + for (auto eltElement : eltPack->getElementTypes()) { + if (first) { + elements.push_back(elt.getWithType(eltElement)); + first = false; + } else { + elements.push_back(TupleTypeElt(eltElement)); + } + } + } else { + elements.push_back(elt.getWithType(transformedEltTy)); + } } if (!anyChanged) return *this; - return TupleType::get(elements, Ptr->getASTContext())->flattenPackTypes(); + // If the transform would yield a singleton tuple, and we didn't + // start with one, flatten to produce the element type. + if (elements.size() == 1 && + !elements[0].getType()->is() && + !(tuple->getNumElements() == 1 && + !tuple->getElementType(0)->is())) { + return elements[0].getType(); + } + + return TupleType::get(elements, Ptr->getASTContext()); } @@ -5794,7 +5923,21 @@ case TypeKind::Id: flags = flags.withInOut(true); } - substParams.emplace_back(substType, label, flags, internalLabel); + if (auto substPack = getTransformedPack(substType)) { + bool first = true; + for (auto substEltType : substPack->getElementTypes()) { + if (first) { + substParams.emplace_back(substEltType, label, flags, + internalLabel); + first = false; + } else { + substParams.emplace_back(substEltType, Identifier(), flags, + Identifier()); + } + } + } else { + substParams.emplace_back(substType, label, flags, internalLabel); + } } // Transform result type. @@ -5836,8 +5979,7 @@ case TypeKind::Id: return GenericFunctionType::get(genericSig, substParams, resultTy); return GenericFunctionType::get(genericSig, substParams, resultTy, function->getExtInfo() - .withGlobalActor(globalActorType)) - ->flattenPackTypes(); + .withGlobalActor(globalActorType)); } if (isUnchanged) return *this; @@ -5846,8 +5988,7 @@ case TypeKind::Id: return FunctionType::get(substParams, resultTy); return FunctionType::get(substParams, resultTy, function->getExtInfo() - .withGlobalActor(globalActorType)) - ->flattenPackTypes(); + .withGlobalActor(globalActorType)); } case TypeKind::ArraySlice: { diff --git a/lib/SIL/IR/SILTypeSubstitution.cpp b/lib/SIL/IR/SILTypeSubstitution.cpp index 51e8d242f6174..b74c754cf6feb 100644 --- a/lib/SIL/IR/SILTypeSubstitution.cpp +++ b/lib/SIL/IR/SILTypeSubstitution.cpp @@ -42,27 +42,6 @@ class SILTypeSubstituter : // context signature. CanGenericSignature Sig; - struct PackExpansion { - /// The shape class of pack parameters that are expanded by this - /// expansion. Set during construction and not changed. - CanType OrigShapeClass; - - /// The count type of the pack expansion in the current lane of - /// expansion, if any. Pack elements in this lane should be - /// expansions with this shape. - CanType SubstPackExpansionCount; - - /// The index of the current lane of expansion. Basic - /// substitution of pack parameters with the same shape as - /// OrigShapeClass should yield a pack, and lanewise - /// substitution should produce this element of that pack. - unsigned Index; - - PackExpansion(CanType origShapeClass) - : OrigShapeClass(origShapeClass), Index(0) {} - }; - SmallVector ActivePackExpansions; - TypeExpansionContext typeExpansionContext; public: @@ -356,63 +335,21 @@ class SILTypeSubstituter : } CanType visitPackExpansionType(CanPackExpansionType origType) { - CanType patternType = visit(origType.getPatternType()); - CanType countType = substASTType(origType.getCountType()); - - return CanType(PackExpansionType::get(patternType, countType)); + llvm_unreachable("shouldn't substitute an independent lowered pack " + "expansion type"); } void substPackExpansion(CanPackExpansionType origType, llvm::function_ref addExpandedType) { - CanType origCountType = origType.getCountType(); - CanType origPatternType = origType.getPatternType(); - - // Substitute the count type (as an AST type). - CanType substCountType = substASTType(origCountType); - - // If that produces a pack type, expand the pattern element-wise. - if (auto substCountPackType = dyn_cast(substCountType)) { - // Set up for element-wise expansion. - ActivePackExpansions.emplace_back(origCountType); - - for (CanType substCountEltType : substCountPackType.getElementTypes()) { - auto expansionType = dyn_cast(substCountEltType); - ActivePackExpansions.back().SubstPackExpansionCount = - (expansionType ? expansionType.getCountType() : CanType()); - - // Expand the pattern type in the element-wise context. - CanType expandedType = visit(origPatternType); - - // Turn that into a pack expansion if appropriate for the - // count element. - if (expansionType) { - expandedType = - CanPackExpansionType::get(expandedType, - expansionType.getCountType()); - } - - addExpandedType(expandedType); - - // Move to the next element. - ActivePackExpansions.back().Index++; + IFS.expandPackExpansionShape(origType.getCountType(), + [&](Type substExpansionShape) { + CanType substComponentType = visit(origType.getPatternType()); + if (substExpansionShape) { + substComponentType = CanPackExpansionType::get(substComponentType, + substExpansionShape->getCanonicalType()); } - - // Leave the element-wise context. - ActivePackExpansions.pop_back(); - return; - } - - // Otherwise, transform the pattern type abstractly and just add a - // type expansion. - CanType substPatternType = visit(origPatternType); - - CanType expandedType; - if (substCountType == origCountType && substPatternType == origPatternType) - expandedType = origType; - else - expandedType = - CanPackExpansionType::get(substPatternType, substCountType); - addExpandedType(expandedType); + addExpandedType(substComponentType); + }); } /// Tuples need to have their component types substituted by these @@ -511,73 +448,12 @@ class SILTypeSubstituter : substType); } - struct SubstRespectingExpansions { - SILTypeSubstituter *_this; - SubstRespectingExpansions(SILTypeSubstituter *_this) : _this(_this) {} - - Type operator()(SubstitutableType *origType) const { - auto substType = _this->IFS.substType(origType); - if (!substType) return substType; - auto substPackType = dyn_cast(substType->getCanonicalType()); - if (!substPackType) return substType; - auto activeExpansion = _this->getActivePackExpansion(CanType(origType)); - if (!activeExpansion) return substType; - auto substEltType = - substPackType.getElementType(activeExpansion->Index); - auto substExpansion = dyn_cast(substEltType); - assert((bool) substExpansion == - (bool) activeExpansion->SubstPackExpansionCount); - if (substExpansion) { - assert(_this->hasSameShape(substExpansion.getCountType(), - activeExpansion->SubstPackExpansionCount)); - return substExpansion.getPatternType(); - } - return substEltType; - } - }; - - struct SubstConformanceRespectingExpansions { - SILTypeSubstituter *_this; - SubstConformanceRespectingExpansions(SILTypeSubstituter *_this) - : _this(_this) {} - - ProtocolConformanceRef operator()(CanType dependentType, - Type conformingReplacementType, - ProtocolDecl *conformingProtocol) const { - auto conformance = - _this->IFS.lookupConformance(dependentType, - conformingReplacementType, - conformingProtocol); - if (!conformance || !conformance.isPack()) return conformance; - auto activeExpansion = _this->getActivePackExpansion(dependentType); - if (!activeExpansion) return conformance; - auto pack = conformance.getPack(); - auto substEltConf = - pack->getPatternConformances()[activeExpansion->Index]; - // There isn't currently a ProtocolConformanceExpansion that - // we would need to look through here. - return substEltConf; - }; - }; - CanType substASTType(CanType origType) { - if (ActivePackExpansions.empty()) - return origType.subst(IFS)->getCanonicalType(); - - return origType.subst(SubstRespectingExpansions(this), - SubstConformanceRespectingExpansions(this), - IFS.getOptions())->getCanonicalType(); + return origType.subst(IFS)->getCanonicalType(); } SubstitutionMap substSubstitutions(SubstitutionMap subs) { - SubstitutionMap newSubs; - - if (ActivePackExpansions.empty()) - newSubs = subs.subst(IFS); - else - newSubs = subs.subst(SubstRespectingExpansions(this), - SubstConformanceRespectingExpansions(this), - IFS.getOptions()); + SubstitutionMap newSubs = subs.subst(IFS); // If we need to look through opaque types in this context, re-substitute // according to the expansion context. @@ -585,28 +461,6 @@ class SILTypeSubstituter : return newSubs; } - - PackExpansion *getActivePackExpansion(CanType dependentType) { - // We push new expansions onto the end of this vector, and we - // want to honor the innermost expansion, so we have to traverse - // in it reverse. - for (auto &entry : reverse(ActivePackExpansions)) { - if (hasSameShape(dependentType, entry.OrigShapeClass)) - return &entry; - } - return nullptr; - } - - bool hasSameShape(CanType lhs, CanType rhs) { - if (lhs->isTypeParameter() && rhs->isTypeParameter()) { - assert(Sig); - return Sig->haveSameShape(lhs, rhs); - } - - auto lhsArchetype = cast(lhs); - auto rhsArchetype = cast(rhs); - return lhsArchetype->getReducedShape() == rhsArchetype->getReducedShape(); - } }; } // end anonymous namespace diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 19b8cd4d83d7b..ef5d9edd47305 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7125,8 +7125,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto *expansion = dyn_cast(expr); auto *elementEnv = expansion->getGenericEnvironment(); - auto toElementType = elementEnv->mapPackTypeIntoElementContext( - toExpansionType->getPatternType()->mapTypeOutOfContext()); + auto toElementType = elementEnv->mapContextualPackTypeIntoElementContext( + toExpansionType->getPatternType()); auto *pattern = coerceToType(expansion->getPatternExpr(), toElementType, locator); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index df47209c617f5..894a7a28f38d7 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3713,16 +3713,56 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, } } -Type ConstraintSystem::simplifyTypeImpl(Type type, - llvm::function_ref getFixedTypeFn) const { - return type.transform([&](Type type) -> Type { - if (auto tvt = dyn_cast(type.getPointer())) - return getFixedTypeFn(tvt); +namespace { + +struct TypeSimplifier { + const ConstraintSystem &CS; + llvm::function_ref GetFixedTypeFn; + + struct ActivePackExpansion { + bool isPackExpansion = false; + unsigned index = 0; + }; + SmallVector ActivePackExpansions; + + TypeSimplifier(const ConstraintSystem &CS, + llvm::function_ref getFixedTypeFn) + : CS(CS), GetFixedTypeFn(getFixedTypeFn) {} + + Type operator()(Type type) { + if (auto tvt = dyn_cast(type.getPointer())) { + auto fixedTy = GetFixedTypeFn(tvt); + + // TODO: the following logic should be applied when rewriting + // PackElementType. + if (ActivePackExpansions.empty()) { + return fixedTy; + } + + if (auto fixedPack = fixedTy->getAs()) { + auto &activeExpansion = ActivePackExpansions.back(); + if (activeExpansion.index >= fixedPack->getNumElements()) { + return tvt; + } + + auto fixedElt = fixedPack->getElementType(activeExpansion.index); + auto fixedExpansion = fixedElt->getAs(); + if (activeExpansion.isPackExpansion && fixedExpansion) { + return fixedExpansion->getPatternType(); + } else if (!activeExpansion.isPackExpansion && !fixedExpansion) { + return fixedElt; + } else { + return tvt; + } + } + + return fixedTy; + } if (auto tuple = dyn_cast(type.getPointer())) { if (tuple->getNumElements() == 1) { auto element = tuple->getElement(0); - auto elementType = simplifyTypeImpl(element.getType(), getFixedTypeFn); + auto elementType = element.getType().transform(*this); // Flatten single-element tuples containing type variables that cannot // bind to packs. @@ -3733,14 +3773,47 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, } } + if (auto expansion = dyn_cast(type.getPointer())) { + // Transform the count type, ignoring any active pack expansions. + auto countType = expansion->getCountType().transform( + TypeSimplifier(CS, GetFixedTypeFn)); + + if (auto countPack = countType->getAs()) { + SmallVector elts; + ActivePackExpansions.push_back({false, 0}); + for (auto countElt : countPack->getElementTypes()) { + auto countExpansion = countElt->getAs(); + ActivePackExpansions.back().isPackExpansion = + (countExpansion != nullptr); + + auto elt = expansion->getPatternType().transform(*this); + if (countExpansion) + elt = PackExpansionType::get(elt, countExpansion->getCountType()); + elts.push_back(elt); + + ActivePackExpansions.back().index++; + } + ActivePackExpansions.pop_back(); + + if (elts.size() == 1) + return elts[0]; + return PackType::get(CS.getASTContext(), elts); + } else { + ActivePackExpansions.push_back({true, 0}); + auto patternType = expansion->getPatternType().transform(*this); + ActivePackExpansions.pop_back(); + return PackExpansionType::get(patternType, countType); + } + } + // If this is a dependent member type for which we end up simplifying // the base to a non-type-variable, perform lookup. if (auto depMemTy = dyn_cast(type.getPointer())) { // Simplify the base. - Type newBase = simplifyTypeImpl(depMemTy->getBase(), getFixedTypeFn); + Type newBase = depMemTy->getBase().transform(*this); if (newBase->isPlaceholder()) { - return PlaceholderType::get(getASTContext(), depMemTy); + return PlaceholderType::get(CS.getASTContext(), depMemTy); } // If nothing changed, we're done. @@ -3760,7 +3833,7 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, if (lookupBaseType->mayHaveMembers() || lookupBaseType->is()) { auto *proto = assocType->getProtocol(); - auto conformance = DC->getParentModule()->lookupConformance( + auto conformance = CS.DC->getParentModule()->lookupConformance( lookupBaseType, proto); if (!conformance) { // FIXME: This regresses diagnostics if removed, but really the @@ -3774,9 +3847,9 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, // so the concrete dependent member type is considered a "hole" in // order to continue solving. auto memberTy = DependentMemberType::get(lookupBaseType, assocType); - if (shouldAttemptFixes() && - getPhase() == ConstraintSystemPhase::Solving) { - return PlaceholderType::get(getASTContext(), memberTy); + if (CS.shouldAttemptFixes() && + CS.getPhase() == ConstraintSystemPhase::Solving) { + return PlaceholderType::get(CS.getASTContext(), memberTy); } return memberTy; @@ -3792,7 +3865,14 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, } return type; - }); + } +}; + +} // end anonymous namespace + +Type ConstraintSystem::simplifyTypeImpl(Type type, + llvm::function_ref getFixedTypeFn) const { + return type.transform(TypeSimplifier(*this, getFixedTypeFn)); } Type ConstraintSystem::simplifyType(Type type) const { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 08315473e14b1..44cead7af37fa 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -918,11 +918,22 @@ static Type applyGenericArguments(Type type, TypeResolution resolution, assert(found != matcher.pairs.end()); auto arg = found->rhs; - if (auto *expansionType = arg->getAs()) - arg = expansionType->getPatternType(); - if (arg->isParameterPack()) - arg = PackType::getSingletonPackExpansion(arg); + // PackMatcher will always produce a PackExpansionType as the + // arg for a pack parameter, if necessary by wrapping a PackType + // in one. (It's a weird representation.) Look for that pattern + // and unwrap the pack. Otherwise, we must have matched with a + // single component which happened to be an expansion; wrap that + // in a PackType. In either case, we always want arg to end up + // a PackType. + if (auto *expansionType = arg->getAs()) { + auto pattern = expansionType->getPatternType(); + if (auto pack = pattern->getAs()) { + arg = pack; + } else { + arg = PackType::get(ctx, {expansionType}); + } + } args.push_back(arg); } From 251c08975374a49647bc113c9fa7e32534c9b7b3 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 25 Mar 2023 18:52:33 -0400 Subject: [PATCH 8/8] Remove some dead code; these operations are now done directly in transformRec. --- include/swift/AST/Types.h | 8 -- lib/AST/ParameterPack.cpp | 179 -------------------------------------- 2 files changed, 187 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 8130c06f51682..5600e9be54237 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2449,8 +2449,6 @@ class TupleType final : public TypeBase, public llvm::FoldingSetNode, bool containsPackExpansionType() const; - Type flattenPackTypes(); - private: TupleType(ArrayRef elements, const ASTContext *CanCtx, RecursiveTypeProperties properties) @@ -3520,8 +3518,6 @@ class AnyFunctionType : public TypeBase { static bool containsPackExpansionType(ArrayRef params); - AnyFunctionType *flattenPackTypes(); - static void printParams(ArrayRef Params, raw_ostream &OS, const PrintOptions &PO = PrintOptions()); static void printParams(ArrayRef Params, ASTPrinter &Printer, @@ -6842,8 +6838,6 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode, bool containsPackExpansionType() const; - PackType *flattenPackTypes(); - CanTypeWrapper getReducedShape(); public: @@ -6938,8 +6932,6 @@ class PackExpansionType : public TypeBase, public llvm::FoldingSetNode { /// Retrieves the count type of this pack expansion. Type getCountType() const { return countType; } - PackExpansionType *expand(); - CanType getReducedShape(); public: diff --git a/lib/AST/ParameterPack.cpp b/lib/AST/ParameterPack.cpp index fb2e93b80219d..5a831a534c14f 100644 --- a/lib/AST/ParameterPack.cpp +++ b/lib/AST/ParameterPack.cpp @@ -94,60 +94,6 @@ PackType *TypeBase::getPackSubstitutionAsPackType() { } } -/// G<{X1, ..., Xn}, {Y1, ..., Yn}>... => {G, ..., G}... -PackExpansionType *PackExpansionType::expand() { - auto countType = getCountType(); - auto *countPack = countType->getAs(); - if (countPack == nullptr) - return this; - - auto patternType = getPatternType(); - if (patternType->is()) - return this; - - unsigned j = 0; - SmallVector expandedTypes; - for (auto type : countPack->getElementTypes()) { - Type expandedCount; - if (auto *expansion = type->getAs()) - expandedCount = expansion->getCountType(); - - auto expandedPattern = patternType.transformRec( - [&](Type t) -> Optional { - if (t->is()) - return t; - - if (auto *nestedPack = t->getAs()) { - auto nestedPackElts = nestedPack->getElementTypes(); - if (j < nestedPackElts.size()) { - if (expandedCount) { - if (auto *expansion = nestedPackElts[j]->getAs()) - return expansion->getPatternType(); - } else { - return nestedPackElts[j]; - } - } - - return ErrorType::get(t->getASTContext()); - } - - return None; - }); - - if (expandedCount) { - expandedTypes.push_back(PackExpansionType::get(expandedPattern, - expandedCount)); - } else { - expandedTypes.push_back(expandedPattern); - } - - ++j; - } - - auto *packType = PackType::get(getASTContext(), expandedTypes); - return PackExpansionType::get(packType, countType); -} - CanType PackExpansionType::getReducedShape() { auto reducedShape = countType->getReducedShape(); if (reducedShape == getASTContext().TheEmptyTupleType) @@ -174,54 +120,6 @@ bool CanTupleType::containsPackExpansionTypeImpl(CanTupleType tuple) { return false; } -/// (W, {X, Y}..., Z) => (W, X, Y, Z) -Type TupleType::flattenPackTypes() { - bool anyChanged = false; - SmallVector elts; - - for (unsigned i = 0, e = getNumElements(); i < e; ++i) { - auto elt = getElement(i); - - if (auto *expansionType = elt.getType()->getAs()) { - if (auto *packType = expansionType->getPatternType()->getAs()) { - if (!anyChanged) { - elts.append(getElements().begin(), getElements().begin() + i); - anyChanged = true; - } - - bool first = true; - for (auto packElt : packType->getElementTypes()) { - if (first) { - elts.push_back(TupleTypeElt(packElt, elt.getName())); - first = false; - continue; - } - elts.push_back(TupleTypeElt(packElt)); - } - - continue; - } - } - - if (anyChanged) - elts.push_back(elt); - } - - if (!anyChanged) - return this; - - // If pack substitution yields a single-element tuple, the tuple - // structure is flattened to produce the element type. - if (elts.size() == 1) { - auto type = elts.front().getType(); - if (!type->is() && !type->is()) { - return type; - } - } - - return TupleType::get(elts, getASTContext()); -} - bool AnyFunctionType::containsPackExpansionType(ArrayRef params) { for (auto param : params) { if (param.getPlainType()->is()) @@ -231,50 +129,6 @@ bool AnyFunctionType::containsPackExpansionType(ArrayRef params) { return false; } -/// (W, {X, Y}..., Z) -> T => (W, X, Y, Z) -> T -AnyFunctionType *AnyFunctionType::flattenPackTypes() { - bool anyChanged = false; - SmallVector params; - - for (unsigned i = 0, e = getParams().size(); i < e; ++i) { - auto param = getParams()[i]; - - if (auto *expansionType = param.getPlainType()->getAs()) { - if (auto *packType = expansionType->getPatternType()->getAs()) { - if (!anyChanged) { - params.append(getParams().begin(), getParams().begin() + i); - anyChanged = true; - } - - bool first = true; - for (auto packElt : packType->getElementTypes()) { - if (first) { - params.push_back(param.withType(packElt)); - first = false; - continue; - } - params.push_back(param.withType(packElt).getWithoutLabels()); - } - - continue; - } - } - - if (anyChanged) - params.push_back(param); - } - - if (!anyChanged) - return this; - - if (auto *genericFuncType = getAs()) { - return GenericFunctionType::get(genericFuncType->getGenericSignature(), - params, getResult(), getExtInfo()); - } else { - return FunctionType::get(params, getResult(), getExtInfo()); - } -} - bool PackType::containsPackExpansionType() const { for (auto type : getElementTypes()) { if (type->is()) @@ -284,39 +138,6 @@ bool PackType::containsPackExpansionType() const { return false; } -/// {W, {X, Y}..., Z} => {W, X, Y, Z} -PackType *PackType::flattenPackTypes() { - bool anyChanged = false; - SmallVector elts; - - for (unsigned i = 0, e = getNumElements(); i < e; ++i) { - auto elt = getElementType(i); - - if (auto *expansionType = elt->getAs()) { - if (auto *packType = expansionType->getPatternType()->getAs()) { - if (!anyChanged) { - elts.append(getElementTypes().begin(), getElementTypes().begin() + i); - anyChanged = true; - } - - for (auto packElt : packType->getElementTypes()) { - elts.push_back(packElt); - } - - continue; - } - } - - if (anyChanged) - elts.push_back(elt); - } - - if (!anyChanged) - return this; - - return PackType::get(getASTContext(), elts); -} - template static CanPackType getReducedShapeOfPack(const ASTContext &ctx, const T &elementTypes) {