Substitution Substitution::subst(Module *module, ArrayRef<Substitution> subs, TypeSubstitutionMap &subMap, ArchetypeConformanceMap &conformanceMap) const { // Substitute the replacement. Type substReplacement = Replacement.subst(module, subMap, None); assert(substReplacement && "substitution replacement failed"); if (substReplacement->isEqual(Replacement)) return *this; if (Conformance.empty()) { return {substReplacement, Conformance}; } bool conformancesChanged = false; SmallVector<ProtocolConformanceRef, 4> substConformances; substConformances.reserve(Conformance.size()); for (auto c : Conformance) { // If we have a concrete conformance, we need to substitute the // conformance to apply to the new type. if (c.isConcrete()) { auto substC = c.getConcrete()->subst(module, substReplacement, subs, subMap, conformanceMap); substConformances.push_back(ProtocolConformanceRef(substC)); if (c != substConformances.back()) conformancesChanged = true; continue; } // Otherwise, we may need to fill in the conformance. ProtocolDecl *proto = c.getAbstract(); Optional<ProtocolConformanceRef> conformance; // If the original type was an archetype, check the conformance map. if (auto replacementArch = Replacement->getAs<ArchetypeType>()) { // Check for conformances for the type that apply to the original // substituted archetype. auto it = conformanceMap.find(replacementArch); assert(it != conformanceMap.end()); for (ProtocolConformanceRef found : it->second) { auto foundProto = found.getRequirement(); if (foundProto == proto) { conformance = found; break; } else if (foundProto->inheritsFrom(proto)) { if (found.isConcrete()) { conformance = ProtocolConformanceRef( found.getConcrete()->getInheritedConformance(proto)); } else { conformance = found; } break; } } } // If that didn't find anything, we can still synthesize AnyObject // conformances from thin air. FIXME: gross. if (!conformance && proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) { auto classDecl = substReplacement->getClassOrBoundGenericClass(); SmallVector<ProtocolConformance *, 1> lookupResults; classDecl->lookupConformance(classDecl->getParentModule(), proto, lookupResults); conformance = ProtocolConformanceRef(lookupResults.front()); } if (conformance) { if (conformance->isConcrete()) conformancesChanged = true; substConformances.push_back(*conformance); } else { assert(substReplacement->hasDependentProtocolConformances() && "couldn't find concrete conformance for concrete type?"); substConformances.push_back(ProtocolConformanceRef(proto)); } } assert(substConformances.size() == Conformance.size()); ArrayRef<ProtocolConformanceRef> substConfs; if (conformancesChanged) substConfs = module->getASTContext().AllocateCopy(substConformances); else substConfs = Conformance; return Substitution{substReplacement, substConfs}; }
Substitution Substitution::subst(Module *module, ArrayRef<Substitution> subs, TypeSubstitutionMap &subMap, ArchetypeConformanceMap &conformanceMap) const { // Substitute the replacement. Type substReplacement = Replacement.subst(module, subMap, None); assert(substReplacement && "substitution replacement failed"); if (substReplacement->isEqual(Replacement)) return *this; bool conformancesChanged = false; SmallVector<ProtocolConformance *, 4> substConformance; substConformance.reserve(Conformance.size()); // When substituting a concrete type for an archetype, we need to fill in the // conformances. if (auto replacementArch = Replacement->getAs<ArchetypeType>()) { if (!substReplacement->hasDependentProtocolConformances()) { // Find the conformances mapped to the archetype. auto found = conformanceMap.find(replacementArch); assert(found != conformanceMap.end() && "no conformances for replaced archetype?!"); auto &foundConformances = found->second; // If the substituted replacement archetype has no conformances, // then there are no conformances to substitute. if (foundConformances.empty()) return Substitution{Archetype, substReplacement, Conformance}; conformancesChanged = true; // Get the conformances for the type that apply to the original // substituted archetype. for (auto proto : Archetype->getConformsTo()) { for (auto c : foundConformances) { if (c->getProtocol() == proto) { substConformance.push_back(c); goto found_conformance; } if (c->getProtocol()->inheritsFrom(proto)) { substConformance.push_back(c->getInheritedConformance(proto)); goto found_conformance; } } // FIXME: AnyObject conformances can be synthesized from // thin air. Gross. if (proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) { auto classDecl = substReplacement->getClassOrBoundGenericClass(); SmallVector<ProtocolConformance *, 1> conformances; classDecl->lookupConformance(classDecl->getParentModule(), proto, conformances); substConformance.push_back(conformances.front()); goto found_conformance; } assert(false && "did not find conformance for archetype requirement?!"); found_conformance:; } } } else { // If we substituted a concrete type for another, we need to substitute the // conformance to apply to the new type. for (auto c : Conformance) { auto substC = c->subst(module, substReplacement, subs, subMap, conformanceMap); if (c != substC) conformancesChanged = true; substConformance.push_back(substC); } } ArrayRef<ProtocolConformance *> substConformanceRef; if (conformancesChanged) substConformanceRef = module->getASTContext().AllocateCopy(substConformance); else substConformanceRef = Conformance; assert(substReplacement->hasDependentProtocolConformances() || substConformanceRef.size() == Archetype->getConformsTo().size()); return Substitution{Archetype, substReplacement, substConformanceRef}; }