Optional<ProtocolConformanceRef> SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const { // Check for conformances for the type that apply to the original // substituted archetype. auto foundReplacement = conformanceMap.find(type.getPointer()); if (foundReplacement != conformanceMap.end()) { auto substReplacement = foundReplacement->second; if (auto conformance = lookupConformance(proto, substReplacement)) return conformance; } // Check if we have substitutions from one of our parent types. return forEachParent(type, [&](CanType parent, AssociatedTypeDecl *assocType) -> Optional<ProtocolConformanceRef> { auto *parentProto = assocType->getProtocol(); auto conformance = lookupConformance(parent, parentProto); if (!conformance) return None; if (!conformance->isConcrete()) return ProtocolConformanceRef(proto); auto sub = conformance->getConcrete()->getTypeWitnessSubstAndDecl( assocType, nullptr).first; return lookupConformance(proto, sub.getConformances()); }); }
Optional<T> SubstitutionMap::forEachParent( CanType type, llvm::SmallPtrSetImpl<CanType> &visitedParents, llvm::function_ref<Optional<T>(CanType, AssociatedTypeDecl *)> fn) const { // If we've already visited the parents of this type, stop. if (!visitedParents.insert(type).second) return None; auto foundParents = parentMap.find(type.getPointer()); if (foundParents != parentMap.end()) { for (auto parent : foundParents->second) { if (auto result = fn(parent.first, parent.second)) return result; } } if (auto archetypeType = dyn_cast<ArchetypeType>(type)) if (auto *parent = archetypeType->getParent()) return fn(CanType(parent), archetypeType->getAssocType()); if (auto memberType = dyn_cast<DependentMemberType>(type)) return fn(CanType(memberType->getBase()), memberType->getAssocType()); return None; }
void SubstitutionMap:: addConformances(CanType type, ArrayRef<ProtocolConformanceRef> conformances) { if (conformances.empty()) return; auto result = conformanceMap.insert( std::make_pair(type.getPointer(), conformances)); assert(result.second); (void) result; }
Optional<ProtocolConformanceRef> SubstitutionMap::forEachParent(CanType type, Fn fn) const { auto foundParents = parentMap.find(type.getPointer()); if (foundParents != parentMap.end()) { for (auto parent : foundParents->second) { if (auto result = fn(parent.first, parent.second)) return result; } } if (auto archetypeType = dyn_cast<ArchetypeType>(type)) if (auto *parent = archetypeType->getParent()) return fn(CanType(parent), archetypeType->getAssocType()); if (auto memberType = dyn_cast<DependentMemberType>(type)) return fn(CanType(memberType->getBase()), memberType->getAssocType()); return None; }
Optional<T> SubstitutionMap::forEachConformance( CanType type, llvm::SmallPtrSetImpl<CanType> &visitedParents, llvm::function_ref<Optional<T>(ProtocolConformanceRef)> fn) const{ // Check for conformances for the type that apply to the original // substituted archetype. auto foundReplacement = conformanceMap.find(type.getPointer()); if (foundReplacement != conformanceMap.end()) { for (auto conformance : foundReplacement->second) { if (auto found = fn(conformance)) return found; } } // Local function to performance a (recursive) search for an associated type // of the given name in the given conformance and all inherited conformances. std::function<Optional<T>(ProtocolConformanceRef, DeclName, llvm::SmallPtrSetImpl<ProtocolDecl *> &)> searchInConformance; searchInConformance = [&](ProtocolConformanceRef conformance, DeclName associatedTypeName, llvm::SmallPtrSetImpl<ProtocolDecl *> &visited) -> Optional<T> { // Only visit a particular protocol once. auto proto = conformance.getRequirement(); if (!visited.insert(proto).second) return None; // Check whether this protocol has an associated type with the // same name as the one we're looking for. AssociatedTypeDecl *protoAssocType = nullptr; for (auto member : proto->lookupDirect(associatedTypeName)) { protoAssocType = dyn_cast<AssociatedTypeDecl>(member); if (protoAssocType) break; } if (protoAssocType) { if (conformance.isAbstract()) { for (auto assocProto : protoAssocType->getConformingProtocols()) { if (auto found = fn(ProtocolConformanceRef(assocProto))) return found; } } else { auto sub = conformance.getConcrete()->getTypeWitnessSubstAndDecl( protoAssocType, nullptr).first; for (auto subConformance : sub.getConformances()) { if (auto found = fn(subConformance)) return found; } } } // Search inherited conformances. for (auto inherited : proto->getInheritedProtocols(nullptr)) { if (auto found = searchInConformance(conformance.getInherited(inherited), associatedTypeName, visited)) return found; } return None; }; // Check if we have conformances from one of our parent types. return forEachParent<ProtocolConformanceRef>(type, visitedParents, [&](CanType parent, AssociatedTypeDecl *assocType) -> Optional<ProtocolConformanceRef> { return forEachConformance<T>(parent, visitedParents, [&](ProtocolConformanceRef conformance) -> Optional<T> { llvm::SmallPtrSet<ProtocolDecl *, 4> visited; return searchInConformance(conformance, assocType->getFullName(), visited); }); }); }
void SubstitutionMap:: addParent(CanType type, CanType parent, AssociatedTypeDecl *assocType) { assert(type && parent && assocType); parentMap[type.getPointer()].push_back(std::make_pair(parent, assocType)); }
ArrayRef<ProtocolConformanceRef> SubstitutionMap:: getConformances(CanType type) const { auto known = conformanceMap.find(type.getPointer()); if (known == conformanceMap.end()) return { }; return known->second; }
void SubstitutionMap:: addConformance(CanType type, ProtocolConformanceRef conformance) { conformanceMap[type.getPointer()].push_back(conformance); }