Skip to content

Commit 910894a

Browse files
authored
Merge pull request #18597 from jrose-apple/well-worn-path
Cache ConformanceAccessPaths in GSB::EquivalenceClass for big wins
2 parents 79e9113 + 5d7f07f commit 910894a

File tree

3 files changed

+168
-152
lines changed

3 files changed

+168
-152
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ class ConformanceAccessPath {
7272
typedef std::pair<Type, ProtocolDecl *> Entry;
7373

7474
private:
75-
llvm::SmallVector<Entry, 2> path;
75+
ArrayRef<Entry> path;
76+
77+
ConformanceAccessPath(ArrayRef<Entry> path) : path(path) {}
7678

7779
friend class GenericSignature;
7880

7981
public:
80-
typedef llvm::SmallVector<Entry, 2>::const_iterator iterator;
81-
typedef llvm::SmallVector<Entry, 2>::const_iterator const_iterator;
82+
typedef const Entry *const_iterator;
83+
typedef const_iterator iterator;
8284

8385
const_iterator begin() const { return path.begin(); }
8486
const_iterator end() const { return path.end(); }
@@ -133,6 +135,13 @@ class alignas(1 << TypeAlignInBits) GenericSignature final
133135
/// Retrieve the generic signature builder for the given generic signature.
134136
GenericSignatureBuilder *getGenericSignatureBuilder();
135137

138+
void buildConformanceAccessPath(
139+
SmallVectorImpl<ConformanceAccessPath::Entry> &path,
140+
ArrayRef<Requirement> reqs,
141+
const void /*GenericSignatureBuilder::RequirementSource*/ *source,
142+
ProtocolDecl *conformingProto, Type rootType,
143+
ProtocolDecl *requirementSignatureProto);
144+
136145
friend class ArchetypeType;
137146

138147
public:

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "swift/AST/Decl.h"
2424
#include "swift/AST/DiagnosticEngine.h"
25+
#include "swift/AST/GenericSignature.h"
2526
#include "swift/AST/Identifier.h"
2627
#include "swift/AST/ProtocolConformanceRef.h"
2728
#include "swift/AST/Types.h"
@@ -45,7 +46,6 @@ namespace swift {
4546
class DeclContext;
4647
class DependentMemberType;
4748
class GenericParamList;
48-
class GenericSignature;
4949
class GenericSignatureBuilder;
5050
class GenericTypeParamType;
5151
class LazyResolver;
@@ -278,6 +278,10 @@ class GenericSignatureBuilder {
278278
/// Cached nested-type information, which contains the best declaration
279279
/// for a given name.
280280
llvm::SmallDenseMap<Identifier, CachedNestedType> nestedTypeNameCache;
281+
282+
/// Cached access paths.
283+
llvm::SmallDenseMap<const ProtocolDecl *, ConformanceAccessPath, 8>
284+
conformanceAccessPathCache;
281285
};
282286

283287
friend class RequirementSource;

lib/AST/GenericSignature.cpp

Lines changed: 151 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -817,175 +817,178 @@ getBestRequirementSource(ArrayRef<GSBConstraint<ProtocolDecl *>> constraints) {
817817
return bestSource;
818818
}
819819

820-
ConformanceAccessPath GenericSignature::getConformanceAccessPath(
821-
Type type,
822-
ProtocolDecl *protocol) {
823-
assert(type->isTypeParameter() && "not a type parameter");
824-
825-
// Resolve this type to a potential archetype.
826-
auto &builder = *getGenericSignatureBuilder();
827-
auto equivClass =
828-
builder.resolveEquivalenceClass(
829-
type,
830-
ArchetypeResolutionKind::CompleteWellFormed);
831-
832-
// Dig out the conformance of this type to the given protocol, because we
833-
// want its requirement source.
834-
auto conforms = equivClass->conformsTo.find(protocol);
835-
assert(conforms != equivClass->conformsTo.end());
836-
837-
// Follow the requirement source to form the conformance access path.
838-
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
839-
ConformanceAccessPath path;
840-
841-
// Local function to construct the conformance access path from the
842-
// requirement.
843-
std::function<void(ArrayRef<Requirement>, const RequirementSource *,
844-
ProtocolDecl *, Type, ProtocolDecl *)> buildPath;
845-
buildPath = [&](ArrayRef<Requirement> reqs, const RequirementSource *source,
846-
ProtocolDecl *conformingProto, Type rootType,
847-
ProtocolDecl *requirementSignatureProto) {
848-
// Each protocol requirement is a step along the path.
849-
if (source->isProtocolRequirement()) {
850-
// If we're expanding for a protocol that had no requirement signature
851-
// and have hit the penultimate step, this is the last step
852-
// that would occur in the requirement signature.
853-
Optional<GenericSignatureBuilder> replacementBuilder;
854-
if (!source->parent->parent && requirementSignatureProto) {
855-
// If we have a requirement signature now, we're done.
856-
if (source->usesRequirementSignature) {
857-
Type subjectType = source->getStoredType()->getCanonicalType();
858-
path.path.push_back({subjectType, conformingProto});
859-
return;
860-
}
861-
862-
// The generic signature builder we're using for this protocol
863-
// wasn't built from its own requirement signature, so we can't
864-
// trust it. Make sure we have a requirement signature, then build
865-
// a new generic signature builder.
866-
// FIXME: It would be better if we could replace the canonical generic
867-
// signature builder with the rebuilt one.
868-
if (!requirementSignatureProto->isRequirementSignatureComputed())
869-
requirementSignatureProto->computeRequirementSignature();
870-
assert(requirementSignatureProto->isRequirementSignatureComputed());
871-
872-
replacementBuilder.emplace(getASTContext());
873-
replacementBuilder->addGenericSignature(
874-
requirementSignatureProto->getGenericSignature());
875-
replacementBuilder->processDelayedRequirements();
876-
}
877-
878-
// Follow the rest of the path to derive the conformance into which
879-
// this particular protocol requirement step would look.
880-
auto inProtocol = source->getProtocolDecl();
881-
buildPath(reqs, source->parent, inProtocol, rootType,
882-
requirementSignatureProto);
883-
assert(path.path.back().second == inProtocol &&
884-
"path produces incorrect conformance");
885-
886-
// If this step was computed via the requirement signature, add it
887-
// directly.
820+
void GenericSignature::buildConformanceAccessPath(
821+
SmallVectorImpl<ConformanceAccessPath::Entry> &path,
822+
ArrayRef<Requirement> reqs, const void *opaqueSource,
823+
ProtocolDecl *conformingProto, Type rootType,
824+
ProtocolDecl *requirementSignatureProto) {
825+
auto *source = reinterpret_cast<const RequirementSource *>(opaqueSource);
826+
// Each protocol requirement is a step along the path.
827+
if (source->isProtocolRequirement()) {
828+
// If we're expanding for a protocol that had no requirement signature
829+
// and have hit the penultimate step, this is the last step
830+
// that would occur in the requirement signature.
831+
Optional<GenericSignatureBuilder> replacementBuilder;
832+
if (!source->parent->parent && requirementSignatureProto) {
833+
// If we have a requirement signature now, we're done.
888834
if (source->usesRequirementSignature) {
889-
// Add this step along the path, which involves looking for the
890-
// conformance we want (\c conformingProto) within the protocol
891-
// described by this source.
892-
893-
// Canonicalize the subject type within the protocol's generic
894-
// signature.
895-
Type subjectType = source->getStoredType();
896-
subjectType = inProtocol->getGenericSignature()
897-
->getCanonicalTypeInContext(subjectType);
898-
899-
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
900-
subjectType, conformingProto) &&
901-
"missing explicit conformance in requirement signature");
902-
903-
// Record this step.
904-
path.path.push_back({subjectType, conformingProto});
835+
Type subjectType = source->getStoredType()->getCanonicalType();
836+
path.push_back({subjectType, conformingProto});
905837
return;
906838
}
907839

908-
// Get the generic signature builder for the protocol.
909-
// Get a generic signature for the protocol's signature.
910-
auto inProtoSig = inProtocol->getGenericSignature();
911-
auto &inProtoSigBuilder =
912-
replacementBuilder ? *replacementBuilder
913-
: *inProtoSig->getGenericSignatureBuilder();
914-
915-
// Retrieve the stored type, but erase all of the specific associated
916-
// type declarations; we don't want any details of the enclosing context
917-
// to sneak in here.
918-
Type storedType = eraseAssociatedTypes(source->getStoredType());
919-
920-
// Dig out the potential archetype for this stored type.
921-
auto equivClass =
922-
inProtoSigBuilder.resolveEquivalenceClass(
923-
storedType,
924-
ArchetypeResolutionKind::CompleteWellFormed);
925-
926-
// Find the conformance of this potential archetype to the protocol in
927-
// question.
928-
auto conforms = equivClass->conformsTo.find(conformingProto);
929-
assert(conforms != equivClass->conformsTo.end());
930-
931-
// Compute the root type, canonicalizing it w.r.t. the protocol context.
932-
auto conformsSource = getBestRequirementSource(conforms->second);
933-
assert(conformsSource != source || !requirementSignatureProto);
934-
Type localRootType = conformsSource->getRootType();
935-
localRootType = inProtoSig->getCanonicalTypeInContext(localRootType);
936-
937-
// Build the path according to the requirement signature.
938-
buildPath(inProtocol->getRequirementSignature(), conformsSource,
939-
conformingProto, localRootType, inProtocol);
940-
941-
// We're done.
942-
return;
840+
// The generic signature builder we're using for this protocol
841+
// wasn't built from its own requirement signature, so we can't
842+
// trust it. Make sure we have a requirement signature, then build
843+
// a new generic signature builder.
844+
// FIXME: It would be better if we could replace the canonical generic
845+
// signature builder with the rebuilt one.
846+
if (!requirementSignatureProto->isRequirementSignatureComputed())
847+
requirementSignatureProto->computeRequirementSignature();
848+
assert(requirementSignatureProto->isRequirementSignatureComputed());
849+
850+
replacementBuilder.emplace(getASTContext());
851+
replacementBuilder->addGenericSignature(
852+
requirementSignatureProto->getGenericSignature());
853+
replacementBuilder->processDelayedRequirements();
943854
}
944855

945-
// If we have a superclass or concrete requirement, the conformance
946-
// we need is stored in it.
947-
if (source->kind == RequirementSource::Superclass ||
948-
source->kind == RequirementSource::Concrete) {
949-
auto conformance = source->getProtocolConformance();
950-
(void)conformance;
951-
assert(conformance.getRequirement() == conformingProto);
952-
path.path.push_back({source->getAffectedType(), conformingProto});
856+
// Follow the rest of the path to derive the conformance into which
857+
// this particular protocol requirement step would look.
858+
auto inProtocol = source->getProtocolDecl();
859+
buildConformanceAccessPath(path, reqs, source->parent, inProtocol, rootType,
860+
requirementSignatureProto);
861+
assert(path.back().second == inProtocol &&
862+
"path produces incorrect conformance");
863+
864+
// If this step was computed via the requirement signature, add it
865+
// directly.
866+
if (source->usesRequirementSignature) {
867+
// Add this step along the path, which involves looking for the
868+
// conformance we want (\c conformingProto) within the protocol
869+
// described by this source.
870+
871+
// Canonicalize the subject type within the protocol's generic
872+
// signature.
873+
Type subjectType = source->getStoredType();
874+
subjectType = inProtocol->getGenericSignature()
875+
->getCanonicalTypeInContext(subjectType);
876+
877+
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
878+
subjectType, conformingProto) &&
879+
"missing explicit conformance in requirement signature");
880+
881+
// Record this step.
882+
path.push_back({subjectType, conformingProto});
953883
return;
954884
}
955885

956-
// If we still have a parent, keep going.
957-
if (source->parent) {
958-
buildPath(reqs, source->parent, conformingProto, rootType,
959-
requirementSignatureProto);
960-
return;
961-
}
886+
// Get the generic signature builder for the protocol.
887+
// Get a generic signature for the protocol's signature.
888+
auto inProtoSig = inProtocol->getGenericSignature();
889+
auto &inProtoSigBuilder =
890+
replacementBuilder ? *replacementBuilder
891+
: *inProtoSig->getGenericSignatureBuilder();
962892

963-
// We are at an explicit or inferred requirement.
964-
assert(source->kind == RequirementSource::Explicit ||
965-
source->kind == RequirementSource::Inferred);
893+
// Retrieve the stored type, but erase all of the specific associated
894+
// type declarations; we don't want any details of the enclosing context
895+
// to sneak in here.
896+
Type storedType = eraseAssociatedTypes(source->getStoredType());
966897

967-
// Skip trivial path elements. These occur when querying a requirement
968-
// signature.
969-
if (!path.path.empty() && conformingProto == path.path.back().second &&
970-
rootType->isEqual(conformingProto->getSelfInterfaceType()))
971-
return;
898+
// Dig out the potential archetype for this stored type.
899+
auto equivClass =
900+
inProtoSigBuilder.resolveEquivalenceClass(
901+
storedType,
902+
ArchetypeResolutionKind::CompleteWellFormed);
972903

973-
assert(hasConformanceInSignature(reqs, rootType, conformingProto) &&
974-
"missing explicit conformance in signature");
904+
// Find the conformance of this potential archetype to the protocol in
905+
// question.
906+
auto conforms = equivClass->conformsTo.find(conformingProto);
907+
assert(conforms != equivClass->conformsTo.end());
975908

976-
// Add the root of the path, which starts at this explicit requirement.
977-
path.path.push_back({rootType, conformingProto});
978-
};
909+
// Compute the root type, canonicalizing it w.r.t. the protocol context.
910+
auto conformsSource = getBestRequirementSource(conforms->second);
911+
assert(conformsSource != source || !requirementSignatureProto);
912+
Type localRootType = conformsSource->getRootType();
913+
localRootType = inProtoSig->getCanonicalTypeInContext(localRootType);
914+
915+
// Build the path according to the requirement signature.
916+
buildConformanceAccessPath(path, inProtocol->getRequirementSignature(),
917+
conformsSource, conformingProto, localRootType,
918+
inProtocol);
919+
920+
// We're done.
921+
return;
922+
}
923+
924+
// If we have a superclass or concrete requirement, the conformance
925+
// we need is stored in it.
926+
if (source->kind == RequirementSource::Superclass ||
927+
source->kind == RequirementSource::Concrete) {
928+
auto conformance = source->getProtocolConformance();
929+
(void)conformance;
930+
assert(conformance.getRequirement() == conformingProto);
931+
path.push_back({source->getAffectedType(), conformingProto});
932+
return;
933+
}
934+
935+
// If we still have a parent, keep going.
936+
if (source->parent) {
937+
buildConformanceAccessPath(path, reqs, source->parent, conformingProto,
938+
rootType, requirementSignatureProto);
939+
return;
940+
}
941+
942+
// We are at an explicit or inferred requirement.
943+
assert(source->kind == RequirementSource::Explicit ||
944+
source->kind == RequirementSource::Inferred);
945+
946+
// Skip trivial path elements. These occur when querying a requirement
947+
// signature.
948+
if (!path.empty() && conformingProto == path.back().second &&
949+
rootType->isEqual(conformingProto->getSelfInterfaceType()))
950+
return;
951+
952+
assert(hasConformanceInSignature(reqs, rootType, conformingProto) &&
953+
"missing explicit conformance in signature");
954+
955+
// Add the root of the path, which starts at this explicit requirement.
956+
path.push_back({rootType, conformingProto});
957+
}
958+
959+
ConformanceAccessPath
960+
GenericSignature::getConformanceAccessPath(Type type, ProtocolDecl *protocol) {
961+
assert(type->isTypeParameter() && "not a type parameter");
962+
963+
// Resolve this type to a potential archetype.
964+
auto &builder = *getGenericSignatureBuilder();
965+
auto equivClass =
966+
builder.resolveEquivalenceClass(
967+
type,
968+
ArchetypeResolutionKind::CompleteWellFormed);
969+
970+
auto cached = equivClass->conformanceAccessPathCache.find(protocol);
971+
if (cached != equivClass->conformanceAccessPathCache.end())
972+
return cached->second;
973+
974+
// Dig out the conformance of this type to the given protocol, because we
975+
// want its requirement source.
976+
auto conforms = equivClass->conformsTo.find(protocol);
977+
assert(conforms != equivClass->conformsTo.end());
979978

980979
// Canonicalize the root type.
981980
auto source = getBestRequirementSource(conforms->second);
982981
Type rootType = source->getRootType()->getCanonicalType(this);
983982

984983
// Build the path.
985-
buildPath(getRequirements(), source, protocol, rootType, nullptr);
984+
SmallVector<ConformanceAccessPath::Entry, 2> path;
985+
buildConformanceAccessPath(path, getRequirements(), source, protocol,
986+
rootType, nullptr);
986987

987988
// Return the path; we're done!
988-
return path;
989+
ConformanceAccessPath result(getASTContext().AllocateCopy(path));
990+
equivClass->conformanceAccessPathCache.insert({protocol, result});
991+
return result;
989992
}
990993

991994
unsigned GenericParamKey::findIndexIn(

0 commit comments

Comments
 (0)