Skip to content

Commit d1cda34

Browse files
committed
RequirementMachine: Property map construction introduces concrete conformance rules
1 parent 2aab4bb commit d1cda34

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,10 @@ PropertyMap::buildPropertyMap(unsigned maxIterations,
388388
// the concrete type witnesses in the concrete type's conformance.
389389
concretizeNestedTypesFromConcreteParents(inducedRules);
390390

391+
// Finally, introduce concrete conformance rules, relating conformance rules
392+
// to concrete type and superclass rules.
393+
recordConcreteConformanceRules(inducedRules);
394+
391395
// Some of the induced rules might be trivial; only count the induced rules
392396
// where the left hand side is not already equivalent to the right hand side.
393397
unsigned addedNewRules = 0;

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ class PropertyMap {
172172
using ConcreteTypeInDomain = std::pair<CanType, ArrayRef<const ProtocolDecl *>>;
173173
llvm::DenseMap<ConcreteTypeInDomain, Term> ConcreteTypeInDomainMap;
174174

175+
llvm::DenseSet<std::pair<unsigned, unsigned>> ConcreteConformanceRules;
176+
175177
DebugOptions Debug;
176178

177179
PropertyBag *getOrCreateProperties(Term key);
@@ -218,6 +220,15 @@ class PropertyMap {
218220
Term key, CanType concreteType, CanType typeWitness,
219221
const MutableTerm &subjectType, ArrayRef<Term> substitutions) const;
220222

223+
void recordConcreteConformanceRules(
224+
SmallVectorImpl<InducedRule> &inducedRules);
225+
226+
void recordConcreteConformanceRule(
227+
unsigned concreteRuleID,
228+
unsigned conformanceRuleID,
229+
const ProtocolDecl *proto,
230+
SmallVectorImpl<InducedRule> &inducedRules);
231+
221232
void verify() const;
222233
};
223234

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,3 +648,152 @@ MutableTerm PropertyMap::computeConstraintTermForTypeWitness(
648648

649649
return constraintType;
650650
}
651+
652+
void PropertyMap::recordConcreteConformanceRules(
653+
SmallVectorImpl<InducedRule> &inducedRules) {
654+
for (const auto &props : Entries) {
655+
if (props->getConformsTo().empty())
656+
continue;
657+
658+
if (props->ConcreteType) {
659+
unsigned concreteRuleID = *props->ConcreteTypeRule;
660+
661+
// The GSB drops all conformance requirements on a type parameter equated
662+
// to a concrete type, even if the concrete type doesn't conform. That is,
663+
//
664+
// protocol P {}
665+
// <T where T : P, T == Int>
666+
//
667+
// minimizes as
668+
//
669+
// <T where T == Int>.
670+
for (unsigned i : indices(props->ConformsTo)) {
671+
auto *proto = props->ConformsTo[i];
672+
auto conformanceRuleID = props->ConformsToRules[i];
673+
recordConcreteConformanceRule(concreteRuleID, conformanceRuleID, proto,
674+
inducedRules);
675+
}
676+
}
677+
678+
if (props->Superclass) {
679+
unsigned superclassRuleID = *props->SuperclassRule;
680+
681+
// For superclass rules, we only introduce a concrete conformance if the
682+
// superclass actually conforms. Otherwise, it is totally fine to have a
683+
// signature like
684+
//
685+
// protocol P {}
686+
// class C {}
687+
// <T where T : P, T : C>
688+
//
689+
// There is no relation between P and C here.
690+
691+
// The conformances in SuperclassConformances should appear in the same
692+
// order as the protocols in ConformsTo.
693+
auto conformanceIter = props->SuperclassConformances.begin();
694+
695+
for (unsigned i : indices(props->ConformsTo)) {
696+
if (conformanceIter == props->SuperclassConformances.end())
697+
break;
698+
699+
auto *proto = props->ConformsTo[i];
700+
if (proto != (*conformanceIter)->getProtocol())
701+
continue;
702+
703+
unsigned conformanceRuleID = props->ConformsToRules[i];
704+
recordConcreteConformanceRule(superclassRuleID, conformanceRuleID, proto,
705+
inducedRules);
706+
++conformanceIter;
707+
}
708+
709+
assert(conformanceIter == props->SuperclassConformances.end());
710+
}
711+
}
712+
}
713+
714+
void PropertyMap::recordConcreteConformanceRule(
715+
unsigned concreteRuleID,
716+
unsigned conformanceRuleID,
717+
const ProtocolDecl *proto,
718+
SmallVectorImpl<InducedRule> &inducedRules) {
719+
if (!ConcreteConformanceRules.insert(
720+
std::make_pair(concreteRuleID, conformanceRuleID)).second) {
721+
// We've already emitted this rule.
722+
return;
723+
}
724+
725+
const auto &concreteRule = System.getRule(concreteRuleID);
726+
const auto &conformanceRule = System.getRule(conformanceRuleID);
727+
728+
auto conformanceSymbol = *conformanceRule.isPropertyRule();
729+
assert(conformanceSymbol.getKind() == Symbol::Kind::Protocol);
730+
assert(conformanceSymbol.getProtocol() == proto);
731+
732+
auto concreteSymbol = *concreteRule.isPropertyRule();
733+
assert(concreteSymbol.getKind() == Symbol::Kind::ConcreteType ||
734+
concreteSymbol.getKind() == Symbol::Kind::Superclass);
735+
736+
RewritePath path;
737+
738+
// We have a pair of rules T.[P] and T'.[concrete: C].
739+
// Either T == T', or T is a prefix of T', or T' is a prefix of T.
740+
//
741+
// Let T'' be the longest of T and T'.
742+
MutableTerm rhs(concreteRule.getRHS().size() > conformanceRule.getRHS().size()
743+
? concreteRule.getRHS()
744+
: conformanceRule.getRHS());
745+
746+
// First, apply the conformance rule in reverse to obtain T''.[P].
747+
path.add(RewriteStep::forRewriteRule(
748+
/*startOffset=*/rhs.size() - conformanceRule.getRHS().size(),
749+
/*endOffset=*/0,
750+
/*ruleID=*/conformanceRuleID,
751+
/*inverse=*/true));
752+
753+
// Now, apply the concrete type rule in reverse to obtain T''.[concrete: C].[P].
754+
path.add(RewriteStep::forRewriteRule(
755+
/*startOffset=*/rhs.size() - concreteRule.getRHS().size(),
756+
/*endOffset=*/1,
757+
/*ruleID=*/concreteRuleID,
758+
/*inverse=*/true));
759+
760+
// Apply a concrete type adjustment to the concrete symbol if T' is shorter
761+
// than T.
762+
unsigned adjustment = rhs.size() - concreteRule.getRHS().size();
763+
if (adjustment > 0 &&
764+
!concreteSymbol.getSubstitutions().empty()) {
765+
// FIXME: This needs an endOffset!
766+
path.add(RewriteStep::forAdjustment(adjustment, /*inverse=*/false));
767+
768+
concreteSymbol = concreteSymbol.prependPrefixToConcreteSubstitutions(
769+
MutableTerm(rhs.begin(), rhs.begin() + adjustment),
770+
Context);
771+
}
772+
773+
// Now, transform T''.[concrete: C].[P] into T''.[concrete: C : P].
774+
Symbol concreteConformanceSymbol = [&]() {
775+
if (concreteSymbol.getKind() == Symbol::Kind::ConcreteType) {
776+
path.add(RewriteStep::forConcreteConformance(/*inverse=*/false));
777+
return Symbol::forConcreteConformance(
778+
concreteSymbol.getConcreteType(),
779+
concreteSymbol.getSubstitutions(),
780+
proto, Context);
781+
} else {
782+
assert(concreteSymbol.getKind() == Symbol::Kind::Superclass);
783+
path.add(RewriteStep::forSuperclassConformance(/*inverse=*/false));
784+
return Symbol::forConcreteConformance(
785+
concreteSymbol.getSuperclass(),
786+
concreteSymbol.getSubstitutions(),
787+
proto, Context);
788+
}
789+
}();
790+
791+
MutableTerm lhs(rhs);
792+
lhs.add(concreteConformanceSymbol);
793+
794+
// The path turns T'' (RHS) into T''.[concrete: C : P] (LHS), but we need
795+
// it to go in the other direction.
796+
path.invert();
797+
798+
inducedRules.emplace_back(std::move(lhs), std::move(rhs), std::move(path));
799+
}

0 commit comments

Comments
 (0)