Skip to content

Commit 499bff2

Browse files
committed
RequirementMachine: Implement GenericSignature::getConformanceAccessPath() query
This is just a straight port of the existing code in the GSB, with minimal changes. It could be made more efficient in the future by trafficking in Terms rather than Types, avoiding some intermediate conversion and canonicalization steps.
1 parent b2ae546 commit 499bff2

File tree

4 files changed

+228
-3
lines changed

4 files changed

+228
-3
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ class ConformanceAccessPath {
8282

8383
friend class GenericSignatureImpl;
8484
friend class GenericSignatureBuilder;
85+
friend class RequirementMachine;
8586

8687
public:
8788
typedef const Entry *const_iterator;
8889
typedef const_iterator iterator;
8990

91+
unsigned size() const { return path.size(); }
9092
const_iterator begin() const { return path.begin(); }
9193
const_iterator end() const { return path.end(); }
9294

include/swift/AST/RequirementMachine.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ class RequirementMachine final {
5555
public:
5656
~RequirementMachine();
5757

58-
// Generic signature queries
58+
// Generic signature queries. Generally you shouldn't have to construct a
59+
// RequirementMachine instance; instead, call the corresponding methods on
60+
// GenericSignature, which lazily create a RequirementMachine for you.
5961
bool requiresClass(Type depType) const;
6062
LayoutConstraint getLayoutConstraint(Type depType) const;
6163
bool requiresProtocol(Type depType, const ProtocolDecl *proto) const;
@@ -66,6 +68,8 @@ class RequirementMachine final {
6668
bool areSameTypeParameterInContext(Type depType1, Type depType2) const;
6769
Type getCanonicalTypeInContext(Type type,
6870
TypeArrayView<GenericTypeParamType> genericParams) const;
71+
ConformanceAccessPath getConformanceAccessPath(Type type,
72+
ProtocolDecl *protocol);
6973
TypeDecl *lookupNestedType(Type depType, Identifier name) const;
7074

7175
void dump(llvm::raw_ostream &out) const;

lib/AST/GenericSignature.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,68 @@ CanGenericSignature::getGenericParams() const{
875875
ConformanceAccessPath
876876
GenericSignatureImpl::getConformanceAccessPath(Type type,
877877
ProtocolDecl *protocol) const {
878-
return getGenericSignatureBuilder()->getConformanceAccessPath(
879-
type, protocol, this);
878+
auto computeViaGSB = [&]() {
879+
return getGenericSignatureBuilder()->getConformanceAccessPath(
880+
type, protocol, this);
881+
};
882+
883+
auto computeViaRQM = [&]() {
884+
auto *machine = getRequirementMachine();
885+
return machine->getConformanceAccessPath(type, protocol);
886+
};
887+
888+
auto &ctx = getASTContext();
889+
if (ctx.LangOpts.EnableRequirementMachine) {
890+
auto rqmResult = computeViaRQM();
891+
892+
#ifndef NDEBUG
893+
auto gsbResult = computeViaGSB();
894+
895+
auto compare = [&]() {
896+
if (gsbResult.size() != rqmResult.size())
897+
return false;
898+
899+
auto *begin1 = gsbResult.begin();
900+
auto *end1 = gsbResult.end();
901+
auto *begin2 = rqmResult.begin();
902+
auto *end2 = rqmResult.end();
903+
904+
while (begin1 < end1) {
905+
assert(begin2 < end2);
906+
907+
if (!begin1->first->isEqual(begin2->first))
908+
return false;
909+
if (begin1->second != begin2->second)
910+
return false;
911+
912+
++begin1;
913+
++begin2;
914+
}
915+
916+
return true;
917+
};
918+
919+
if (!compare()) {
920+
llvm::errs() << "RequirementMachine::getConformanceAccessPath() is broken\n";
921+
llvm::errs() << "Generic signature: " << GenericSignature(this) << "\n";
922+
llvm::errs() << "Dependent type: "; type.dump(llvm::errs());
923+
llvm::errs() << "Protocol: "; protocol->dumpRef(llvm::errs());
924+
llvm::errs() << "\n";
925+
llvm::errs() << "GenericSignatureBuilder says: ";
926+
gsbResult.print(llvm::errs());
927+
llvm::errs() << "\n";
928+
llvm::errs() << "RequirementMachine says: ";
929+
rqmResult.print(llvm::errs());
930+
llvm::errs() << "\n\n";
931+
getRequirementMachine()->dump(llvm::errs());
932+
abort();
933+
}
934+
#endif
935+
936+
return rqmResult;
937+
} else {
938+
return computeViaGSB();
939+
}
880940
}
881941

882942
TypeDecl *

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ struct RequirementMachine::Implementation {
233233
CanGenericSignature Sig;
234234
bool Complete = false;
235235

236+
/// All conformance access paths computed so far.
237+
llvm::DenseMap<std::pair<CanType, ProtocolDecl *>,
238+
ConformanceAccessPath> ConformanceAccessPaths;
239+
240+
/// Conformance access paths computed during the last round. All elements
241+
/// have the same length. If a conformance access path of greater length
242+
/// is requested, we refill CurrentConformanceAccessPaths with all paths of
243+
/// length N+1, and add them to the ConformanceAccessPaths map.
244+
std::vector<std::pair<CanType, ConformanceAccessPath>>
245+
CurrentConformanceAccessPaths;
246+
236247
explicit Implementation(ASTContext &ctx)
237248
: Context(ctx),
238249
System(Context),
@@ -748,6 +759,154 @@ Type RequirementMachine::getCanonicalTypeInContext(
748759
});
749760
}
750761

762+
/// Replace 'Self' in the given dependent type (\c depTy) with the given
763+
/// dependent type, producing a type that refers to
764+
/// the nested type. This limited operation makes sure that it does not
765+
/// create any new potential archetypes along the way, so it should only be
766+
/// used in cases where we're reconstructing something that we know exists.
767+
static Type replaceSelfWithType(Type selfType, Type depTy) {
768+
if (auto depMemTy = depTy->getAs<DependentMemberType>()) {
769+
Type baseType = replaceSelfWithType(selfType, depMemTy->getBase());
770+
assert(depMemTy->getAssocType() && "Missing associated type");
771+
return DependentMemberType::get(baseType, depMemTy->getAssocType());
772+
}
773+
774+
assert(depTy->is<GenericTypeParamType>() && "missing Self?");
775+
return selfType;
776+
}
777+
778+
/// Retrieve the conformance access path used to extract the conformance of
779+
/// interface \c type to the given \c protocol.
780+
///
781+
/// \param type The interface type whose conformance access path is to be
782+
/// queried.
783+
/// \param protocol A protocol to which \c type conforms.
784+
///
785+
/// \returns the conformance access path that starts at a requirement of
786+
/// this generic signature and ends at the conformance that makes \c type
787+
/// conform to \c protocol.
788+
///
789+
/// \seealso ConformanceAccessPath
790+
ConformanceAccessPath
791+
RequirementMachine::getConformanceAccessPath(Type type,
792+
ProtocolDecl *protocol) {
793+
auto canType = getCanonicalTypeInContext(type, { })->getCanonicalType();
794+
assert(canType->isTypeParameter());
795+
796+
// Check if we've already cached the result before doing anything else.
797+
auto found = Impl->ConformanceAccessPaths.find(
798+
std::make_pair(canType, protocol));
799+
if (found != Impl->ConformanceAccessPaths.end()) {
800+
return found->second;
801+
}
802+
803+
auto *Stats = Context.Stats;
804+
805+
FrontendStatsTracer tracer(Stats, "get-conformance-access-path");
806+
807+
auto recordPath = [&](CanType type, ProtocolDecl *proto,
808+
ConformanceAccessPath path) {
809+
// Add the path to the buffer.
810+
Impl->CurrentConformanceAccessPaths.emplace_back(type, path);
811+
812+
// Add the path to the map.
813+
auto key = std::make_pair(type, proto);
814+
auto inserted = Impl->ConformanceAccessPaths.insert(
815+
std::make_pair(key, path));
816+
assert(inserted.second);
817+
(void) inserted;
818+
819+
if (Stats)
820+
++Stats->getFrontendCounters().NumConformanceAccessPathsRecorded;
821+
};
822+
823+
// If this is the first time we're asked to look up a conformance access path,
824+
// visit all of the root conformance requirements in our generic signature and
825+
// add them to the buffer.
826+
if (Impl->ConformanceAccessPaths.empty()) {
827+
for (const auto &req : Impl->Sig->getRequirements()) {
828+
// We only care about conformance requirements.
829+
if (req.getKind() != RequirementKind::Conformance)
830+
continue;
831+
832+
auto rootType = CanType(req.getFirstType());
833+
auto *rootProto = req.getProtocolDecl();
834+
835+
ConformanceAccessPath::Entry root(rootType, rootProto);
836+
ArrayRef<ConformanceAccessPath::Entry> path(root);
837+
ConformanceAccessPath result(Context.AllocateCopy(path));
838+
839+
recordPath(rootType, rootProto, result);
840+
}
841+
}
842+
843+
// We enumerate conformance access paths in lexshort order until we find the
844+
// path whose corresponding type canonicalizes to the one we are looking for.
845+
while (true) {
846+
auto found = Impl->ConformanceAccessPaths.find(
847+
std::make_pair(canType, protocol));
848+
if (found != Impl->ConformanceAccessPaths.end()) {
849+
return found->second;
850+
}
851+
852+
assert(Impl->CurrentConformanceAccessPaths.size() > 0);
853+
854+
// The buffer consists of all conformance access paths of length N.
855+
// Swap it out with an empty buffer, and fill it with all paths of
856+
// length N+1.
857+
std::vector<std::pair<CanType, ConformanceAccessPath>> oldPaths;
858+
std::swap(Impl->CurrentConformanceAccessPaths, oldPaths);
859+
860+
for (const auto &pair : oldPaths) {
861+
const auto &lastElt = pair.second.back();
862+
auto *lastProto = lastElt.second;
863+
864+
// A copy of the current path, populated as needed.
865+
SmallVector<ConformanceAccessPath::Entry, 4> entries;
866+
867+
for (const auto &req : lastProto->getRequirementSignature()) {
868+
// We only care about conformance requirements.
869+
if (req.getKind() != RequirementKind::Conformance)
870+
continue;
871+
872+
auto nextSubjectType = req.getFirstType()->getCanonicalType();
873+
auto *nextProto = req.getProtocolDecl();
874+
875+
// Compute the canonical anchor for this conformance requirement.
876+
auto nextType = replaceSelfWithType(pair.first, nextSubjectType);
877+
auto nextCanType = getCanonicalTypeInContext(nextType, { })
878+
->getCanonicalType();
879+
880+
// Skip "derived via concrete" sources.
881+
if (!nextCanType->isTypeParameter())
882+
continue;
883+
884+
// If we've already seen a path for this conformance, skip it and
885+
// don't add it to the buffer. Note that because we iterate over
886+
// conformance access paths in lexshort order, the existing
887+
// conformance access path is shorter than the one we found just now.
888+
if (Impl->ConformanceAccessPaths.count(
889+
std::make_pair(nextCanType, nextProto)))
890+
continue;
891+
892+
if (entries.empty()) {
893+
// Fill our temporary vector.
894+
entries.insert(entries.begin(),
895+
pair.second.begin(),
896+
pair.second.end());
897+
}
898+
899+
// Add the next entry.
900+
entries.emplace_back(nextSubjectType, nextProto);
901+
ConformanceAccessPath result = Context.AllocateCopy(entries);
902+
entries.pop_back();
903+
904+
recordPath(nextCanType, nextProto, result);
905+
}
906+
}
907+
}
908+
}
909+
751910
/// Compare two associated types.
752911
static int compareAssociatedTypes(AssociatedTypeDecl *assocType1,
753912
AssociatedTypeDecl *assocType2) {

0 commit comments

Comments
 (0)