diff --git a/include/swift/AST/Substitution.h b/include/swift/AST/Substitution.h index 9ba7f21fd316c..232d61a625456 100644 --- a/include/swift/AST/Substitution.h +++ b/include/swift/AST/Substitution.h @@ -19,6 +19,7 @@ #include "swift/AST/Type.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" namespace llvm { class raw_ostream; @@ -28,11 +29,30 @@ namespace swift { class ArchetypeType; class GenericEnvironment; class ProtocolConformanceRef; - -/// DenseMap type used internally by Substitution::subst to track conformances -/// applied to archetypes. -using ArchetypeConformanceMap - = llvm::DenseMap>; + +/// Data structure type used internally by Substitution::subst to track +/// conformances applied to archetypes. +class ArchetypeConformanceMap { + using ParentType = std::pair; + + llvm::DenseMap> map; + llvm::DenseMap> parents; + + Optional + lookupArchetypeConformance(ProtocolDecl *proto, + ArrayRef conformances) const; + +public: + Optional + lookupArchetypeConformance(ArchetypeType *replacement, + ProtocolDecl *proto) const; + + void addArchetypeConformances(ArchetypeType *replacement, + ArrayRef conformances); + + void addArchetypeParent(ArchetypeType *replacement, ArchetypeType *parent, + AssociatedTypeDecl *assocType); +}; /// Substitution - A substitution into a generic specialization. class Substitution { diff --git a/lib/AST/GenericEnvironment.cpp b/lib/AST/GenericEnvironment.cpp index 416841163c5e8..16eaffd7536bd 100644 --- a/lib/AST/GenericEnvironment.cpp +++ b/lib/AST/GenericEnvironment.cpp @@ -98,8 +98,39 @@ getSubstitutionMap(ModuleDecl *mod, // Record the replacement type and its conformances. subsMap[archetype] = sub.getReplacement(); - conformanceMap[archetype] = sub.getConformances(); + conformanceMap.addArchetypeConformances(archetype, sub.getConformances()); } - + + for (auto reqt : sig->getRequirements()) { + if (reqt.getKind() != RequirementKind::SameType) + continue; + + auto first = reqt.getFirstType()->getAs(); + auto second = reqt.getSecondType()->getAs(); + + if (!first || !second) + continue; + + auto archetype = mapTypeIntoContext(mod, first)->getAs(); + if (!archetype) + continue; + + auto firstBase = first->getBase(); + auto secondBase = second->getBase(); + + auto firstBaseArchetype = mapTypeIntoContext(mod, firstBase)->getAs(); + auto secondBaseArchetype = mapTypeIntoContext(mod, secondBase)->getAs(); + + if (!firstBaseArchetype || !secondBaseArchetype) + continue; + + if (archetype->getParent() != firstBaseArchetype) + conformanceMap.addArchetypeParent(archetype, firstBaseArchetype, + first->getAssocType()); + if (archetype->getParent() != secondBaseArchetype) + conformanceMap.addArchetypeParent(archetype, secondBaseArchetype, + second->getAssocType()); + } + assert(subs.empty() && "did not use all substitutions?!"); } diff --git a/lib/AST/Substitution.cpp b/lib/AST/Substitution.cpp index 82b0c1f78dd65..33e7c4f924c01 100644 --- a/lib/AST/Substitution.cpp +++ b/lib/AST/Substitution.cpp @@ -24,38 +24,9 @@ using namespace swift; -bool Substitution::operator==(const Substitution &other) const { - // The archetypes may be missing, but we can compare them directly - // because archetypes are always canonical. - return - Replacement->getCanonicalType() == other.Replacement->getCanonicalType() && - Conformance.equals(other.Conformance); -} - -Substitution::Substitution(Type Replacement, - ArrayRef Conformance) - : Replacement(Replacement), Conformance(Conformance) -{ - // The replacement type must be materializable. - assert(Replacement->isMaterializable() - && "cannot substitute with a non-materializable type"); -} - -Substitution Substitution::subst(Module *module, - GenericSignature *sig, - GenericEnvironment *env, - ArrayRef subs) const { - TypeSubstitutionMap subMap; - ArchetypeConformanceMap conformanceMap; - - assert(sig && env); - env->getSubstitutionMap(module, sig, subs, subMap, conformanceMap); - return subst(module, subMap, conformanceMap); -} - -static Optional +Optional ArchetypeConformanceMap:: lookupArchetypeConformance(ProtocolDecl *proto, - ArrayRef conformances) { + ArrayRef conformances) const { for (ProtocolConformanceRef found : conformances) { auto foundProto = found.getRequirement(); if (foundProto == proto) { @@ -73,40 +44,93 @@ lookupArchetypeConformance(ProtocolDecl *proto, return None; } -static Optional +Optional ArchetypeConformanceMap:: lookupArchetypeConformance(ArchetypeType *replacement, - ProtocolDecl *proto, - ArchetypeConformanceMap &conformanceMap) { + ProtocolDecl *proto) const { // Check for conformances for the type that apply to the original // substituted archetype. - auto it = conformanceMap.find(replacement); - if (it != conformanceMap.end()) { - if (auto conformance = lookupArchetypeConformance(proto, it->second)) { + auto foundReplacement = map.find(replacement); + if (foundReplacement != map.end()) { + auto substReplacement = foundReplacement->second; + if (auto conformance = lookupArchetypeConformance(proto, substReplacement)) return conformance; - } } - // Check if we have substitutions for the parent. - if (auto *parent = replacement->getParent()) { - auto *assocType = replacement->getAssocType(); - auto *parentProto = assocType->getProtocol(); + // Check if we have substitutions from one of our parent archetypes. + auto foundParents = parents.find(replacement); + if (foundParents == parents.end()) + return None; + + for (auto parent : foundParents->second) { + auto *parentProto = parent.second->getProtocol(); auto conformance = - lookupArchetypeConformance(parent, parentProto, conformanceMap); + lookupArchetypeConformance(parent.first, parentProto); if (conformance) { if (!conformance->isConcrete()) return ProtocolConformanceRef(proto); auto sub = conformance->getConcrete()->getTypeWitnessSubstAndDecl( - assocType, nullptr).first; + parent.second, nullptr).first; - return lookupArchetypeConformance(proto, sub.getConformances()); + if (auto result = lookupArchetypeConformance(proto, sub.getConformances())) + return result; } } return None; } +void ArchetypeConformanceMap:: +addArchetypeConformances(ArchetypeType *replacement, + ArrayRef conformances) { + assert(replacement); + + auto result = map.insert(std::make_pair(replacement, conformances)); + assert(result.second); + (void) result; + + if (auto *parent = replacement->getParent()) + addArchetypeParent(replacement, parent, replacement->getAssocType()); +} + +void ArchetypeConformanceMap:: +addArchetypeParent(ArchetypeType *replacement, + ArchetypeType *parent, + AssociatedTypeDecl *assocType) { + assert(replacement && parent && assocType); + parents[replacement].push_back(std::make_pair(parent, assocType)); +} + +bool Substitution::operator==(const Substitution &other) const { + // The archetypes may be missing, but we can compare them directly + // because archetypes are always canonical. + return + Replacement->getCanonicalType() == other.Replacement->getCanonicalType() && + Conformance.equals(other.Conformance); +} + +Substitution::Substitution(Type Replacement, + ArrayRef Conformance) + : Replacement(Replacement), Conformance(Conformance) +{ + // The replacement type must be materializable. + assert(Replacement->isMaterializable() + && "cannot substitute with a non-materializable type"); +} + +Substitution Substitution::subst(Module *module, + GenericSignature *sig, + GenericEnvironment *env, + ArrayRef subs) const { + TypeSubstitutionMap subMap; + ArchetypeConformanceMap conformanceMap; + + assert(sig && env); + env->getSubstitutionMap(module, sig, subs, subMap, conformanceMap); + return subst(module, subMap, conformanceMap); +} + Substitution Substitution::subst(Module *module, TypeSubstitutionMap &subMap, ArchetypeConformanceMap &conformanceMap) const { @@ -143,8 +167,8 @@ Substitution Substitution::subst(Module *module, // If the original type was an archetype, check the conformance map. if (auto replacementArch = Replacement->getAs()) { - conformance = lookupArchetypeConformance(replacementArch, proto, - conformanceMap); + conformance = conformanceMap.lookupArchetypeConformance( + replacementArch, proto); } // If that didn't find anything, we can still synthesize AnyObject diff --git a/test/SILOptimizer/specialize_same_type_constraint.swift b/test/SILOptimizer/specialize_same_type_constraint.swift new file mode 100644 index 0000000000000..6e4cd75aee96a --- /dev/null +++ b/test/SILOptimizer/specialize_same_type_constraint.swift @@ -0,0 +1,53 @@ +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s | %FileCheck %s + +protocol FirstChild {} + +protocol FirstParent { + associatedtype Child : FirstChild + + var firstChild: Child { get } +} + +protocol SecondChild {} + +protocol SecondParent { + associatedtype Child : SecondChild + + var secondChild: Child { get } +} + +@_semantics("optimize.sil.never") +func takesFirstChild(t: T) {} + +@_semantics("optimize.sil.never") +func takesSecondChild(t: T) {} + +@inline(never) +func doStuff(f: First, s: Second) + where First.Child == Second.Child { + takesFirstChild(t: f.firstChild) + takesSecondChild(t: f.firstChild) + + takesFirstChild(t: s.secondChild) + takesSecondChild(t: s.secondChild) +} + +struct ConcreteChild : FirstChild, SecondChild {} + +struct ConcreteFirstParent : FirstParent { + var firstChild: ConcreteChild { return ConcreteChild() } +} + +struct ConcreteSecondParent : SecondParent { + var secondChild: ConcreteChild { return ConcreteChild() } +} + +doStuff(f: ConcreteFirstParent(), + s: ConcreteSecondParent()) + +// CHECK-LABEL: sil shared [noinline] @_TTSf4d_d___TTSg5GV31specialize_same_type_constraint19ConcreteFirstParentVS_13ConcreteChild_GS0_S1__S_11FirstParentS__GVS_20ConcreteSecondParentS1__GS3_S1__S_12SecondParentS____TF31specialize_same_type_constraint7doStuffu0_RxS_11FirstParent_S_12SecondParentwx5Childzw_5ChildrFT1fx1sq__T_ +// CHECK: [[FIRST:%.*]] = function_ref @_TF31specialize_same_type_constraint15takesFirstChilduRxS_10FirstChildrFT1tx_T_ +// CHECK: apply [[FIRST]]({{.*}}) : $@convention(thin) <τ_0_0 where τ_0_0 : FirstChild> (@in τ_0_0) -> () +// CHECK: [[SECOND:%.*]] = function_ref @_TF31specialize_same_type_constraint16takesSecondChilduRxS_11SecondChildrFT1tx_T_ +// CHECK: apply [[SECOND]]({{.*}}) : $@convention(thin) <τ_0_0 where τ_0_0 : SecondChild> (@in τ_0_0) -> () +// CHECK: return