3030// they are added by completion, or they are redundant rules written by the
3131// user.
3232//
33- // Using the 3-cells that generate the homotopy relation on rewrite paths,
33+ // Using the rewrite loops that generate the homotopy relation on rewrite paths,
3434// decompositions can be found for all "derived" conformance rules, producing
3535// a minimal set of generating conformances.
3636//
6969using namespace swift ;
7070using namespace rewriting ;
7171
72- // / Finds all protocol conformance rules appearing in a 3-cell , both without
73- // / context, and with a non-empty left context. Applications of rules with a
74- // / non-empty right context are ignored.
72+ // / Finds all protocol conformance rules appearing in a rewrite loop , both
73+ // / in empty context, and with a non-empty left context. Applications of rules
74+ // / with a non-empty right context are ignored.
7575// /
7676// / The rules are organized by protocol. For each protocol, the first element
7777// / of the pair stores conformance rules that appear without context. The
7878// / second element of the pair stores rules that appear with non-empty left
7979// / context. For each such rule, the left prefix is also stored alongside.
80- void HomotopyGenerator ::findProtocolConformanceRules (
80+ void RewriteLoop ::findProtocolConformanceRules (
8181 llvm::SmallDenseMap<const ProtocolDecl *,
82- std::pair<SmallVector<unsigned , 2 >,
83- SmallVector<std::pair<MutableTerm, unsigned >, 2 >>>
84- &result,
82+ ProtocolConformanceRules, 2 > &result,
8583 const RewriteSystem &system) const {
8684
87- auto redundantRules = Path. findRulesAppearingOnceInEmptyContext ();
85+ auto redundantRules = findRulesAppearingOnceInEmptyContext (system );
8886
8987 bool foundAny = false ;
9088 for (unsigned ruleID : redundantRules) {
9189 const auto &rule = system.getRule (ruleID);
90+
9291 if (auto *proto = rule.isProtocolConformanceRule ()) {
93- result[proto].first .push_back (ruleID);
92+ if (rule.isIdentityConformanceRule ()) {
93+ result[proto].SawIdentityConformance = true ;
94+ continue ;
95+ }
96+
97+ result[proto].RulesInEmptyContext .push_back (ruleID);
9498 foundAny = true ;
9599 }
96100 }
97101
98102 if (!foundAny)
99103 return ;
100104
101- MutableTerm term = Basepoint;
105+ RewritePathEvaluator evaluator ( Basepoint) ;
102106
103107 // Now look for rewrite steps with conformance rules in empty right context,
104- // that is something like X.(Y.[P] => Z ) (or it's inverse, X.(Z => Y.[P])).
108+ // that is something like X.(Y.[P] => Y ) (or it's inverse, X.(Y => Y.[P])).
105109 for (const auto &step : Path) {
106- switch (step.Kind ) {
107- case RewriteStep::ApplyRewriteRule: {
108- const auto &rule = system.getRule (step.RuleID );
109- if (auto *proto = rule.isProtocolConformanceRule ()) {
110- if (step.StartOffset > 0 &&
111- step.EndOffset == 0 ) {
112- // Record the prefix term that is left unchanged by this rewrite step.
113- //
114- // In the above example where the rewrite step is X.(Y.[P] => Z),
115- // the prefix term is 'X'.
116- MutableTerm prefix (term.begin (), term.begin () + step.StartOffset );
117- result[proto].second .emplace_back (prefix, step.RuleID );
110+ if (!evaluator.isInContext ()) {
111+ switch (step.Kind ) {
112+ case RewriteStep::ApplyRewriteRule: {
113+ const auto &rule = system.getRule (step.RuleID );
114+
115+ if (rule.isIdentityConformanceRule ())
116+ break ;
117+
118+ if (auto *proto = rule.isProtocolConformanceRule ()) {
119+ if (step.StartOffset > 0 &&
120+ step.EndOffset == 0 ) {
121+ // Record the prefix term that is left unchanged by this rewrite step.
122+ //
123+ // In the above example where the rewrite step is X.(Y.[P] => Z),
124+ // the prefix term is 'X'.
125+ const auto &term = evaluator.getCurrentTerm ();
126+ MutableTerm prefix (term.begin (), term.begin () + step.StartOffset );
127+ result[proto].RulesInContext .emplace_back (prefix, step.RuleID );
128+ }
118129 }
119- }
120130
121- break ;
122- }
131+ break ;
132+ }
123133
124- case RewriteStep::AdjustConcreteType:
125- break ;
134+ case RewriteStep::AdjustConcreteType:
135+ case RewriteStep::Shift:
136+ case RewriteStep::Decompose:
137+ break ;
138+ }
126139 }
127140
128- step.apply (term , system);
141+ step.apply (evaluator , system);
129142 }
130143}
131144
@@ -152,6 +165,13 @@ RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
152165 " Canonical conformance term should simplify in one step" );
153166
154167 const auto &step = *steps.begin ();
168+
169+ #ifndef NDEBUG
170+ const auto &rule = getRule (step.RuleID );
171+ assert (rule.isProtocolConformanceRule ());
172+ assert (!rule.isIdentityConformanceRule ());
173+ #endif
174+
155175 assert (step.Kind == RewriteStep::ApplyRewriteRule);
156176 assert (step.EndOffset == 0 );
157177 assert (!step.Inverse );
@@ -177,6 +197,7 @@ RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
177197 SmallVectorImpl<unsigned > &result) const {
178198 const auto &rule = getRule (ruleID);
179199 assert (rule.isProtocolConformanceRule ());
200+ assert (!rule.isIdentityConformanceRule ());
180201
181202 // Compute domain(V).
182203 const auto &lhs = rule.getLHS ();
@@ -253,14 +274,12 @@ RewriteSystem::decomposeTermIntoConformanceRuleLeftHandSides(
253274void RewriteSystem::computeCandidateConformancePaths (
254275 llvm::MapVector<unsigned ,
255276 std::vector<SmallVector<unsigned , 2 >>> &conformancePaths) const {
256- for (const auto &loop : HomotopyGenerators ) {
277+ for (const auto &loop : Loops ) {
257278 if (loop.isDeleted ())
258279 continue ;
259280
260281 llvm::SmallDenseMap<const ProtocolDecl *,
261- std::pair<SmallVector<unsigned , 2 >,
262- SmallVector<std::pair<MutableTerm, unsigned >, 2 >>>
263- result;
282+ ProtocolConformanceRules, 2 > result;
264283
265284 loop.findProtocolConformanceRules (result, *this );
266285
@@ -275,21 +294,18 @@ void RewriteSystem::computeCandidateConformancePaths(
275294
276295 for (const auto &pair : result) {
277296 const auto *proto = pair.first ;
278- const auto ¬InContext = pair.second .first ;
279- const auto &inContext = pair.second .second ;
297+ const auto &inEmptyContext = pair.second .RulesInEmptyContext ;
298+ const auto &inContext = pair.second .RulesInContext ;
299+ bool sawIdentityConformance = pair.second .SawIdentityConformance ;
280300
281301 // No rules appear without context.
282- if (notInContext.empty ())
283- continue ;
284-
285- // No replacement rules.
286- if (notInContext.size () == 1 && inContext.empty ())
302+ if (inEmptyContext.empty ())
287303 continue ;
288304
289305 if (Debug.contains (DebugFlags::GeneratingConformances)) {
290306 llvm::dbgs () << " * Protocol " << proto->getName () << " :\n " ;
291307 llvm::dbgs () << " ** Conformance rules not in context:\n " ;
292- for (unsigned ruleID : notInContext ) {
308+ for (unsigned ruleID : inEmptyContext ) {
293309 llvm::dbgs () << " -- (#" << ruleID << " ) " << getRule (ruleID) << " \n " ;
294310 }
295311
@@ -300,6 +316,10 @@ void RewriteSystem::computeCandidateConformancePaths(
300316 llvm::dbgs () << " (#" << ruleID << " ) " << getRule (ruleID) << " \n " ;
301317 }
302318
319+ if (sawIdentityConformance) {
320+ llvm::dbgs () << " ** Equivalent to identity conformance\n " ;
321+ }
322+
303323 llvm::dbgs () << " \n " ;
304324 }
305325
@@ -310,8 +330,8 @@ void RewriteSystem::computeCandidateConformancePaths(
310330 //
311331 // (T.[P] => T) := (T'.[P])
312332 // (T'.[P] => T') := (T.[P])
313- for (unsigned candidateRuleID : notInContext ) {
314- for (unsigned otherRuleID : notInContext ) {
333+ for (unsigned candidateRuleID : inEmptyContext ) {
334+ for (unsigned otherRuleID : inEmptyContext ) {
315335 if (otherRuleID == candidateRuleID)
316336 continue ;
317337
@@ -321,11 +341,22 @@ void RewriteSystem::computeCandidateConformancePaths(
321341 }
322342 }
323343
324- // Suppose a 3-cell contains a conformance rule (T.[P] => T) in an empty
325- // context, and a conformance rule (V.[P] => V) with a non-empty left
344+ // If a rewrite loop contains a conformance rule (T.[P] => T) together
345+ // with the identity conformance ([P].[P] => [P]), both in empty context,
346+ // the conformance rule (T.[P] => T) is equivalent to the *empty product*
347+ // of conformance rules; that is, it is trivially redundant.
348+ if (sawIdentityConformance) {
349+ for (unsigned candidateRuleID : inEmptyContext) {
350+ SmallVector<unsigned , 2 > emptyPath;
351+ conformancePaths[candidateRuleID].push_back (emptyPath);
352+ }
353+ }
354+
355+ // Suppose a rewrite loop contains a conformance rule (T.[P] => T) in
356+ // empty context, and a conformance rule (V.[P] => V) in non-empty left
326357 // context U.
327358 //
328- // The 3-cell looks something like this:
359+ // The rewrite loop looks something like this:
329360 //
330361 // ... ⊗ (T.[P] => T) ⊗ ... ⊗ U.(V => V.[P]) ⊗ ...
331362 // ^ ^
@@ -360,7 +391,7 @@ void RewriteSystem::computeCandidateConformancePaths(
360391
361392 // This decomposition defines a conformance access path for each
362393 // conformance rule we saw in empty context.
363- for (unsigned otherRuleID : notInContext )
394+ for (unsigned otherRuleID : inEmptyContext )
364395 conformancePaths[otherRuleID].push_back (conformancePath);
365396 }
366397 }
@@ -449,6 +480,11 @@ bool RewriteSystem::isValidRefinementPath(
449480void RewriteSystem::dumpConformancePath (
450481 llvm::raw_ostream &out,
451482 const SmallVectorImpl<unsigned > &path) const {
483+ if (path.empty ()) {
484+ out << " 1" ;
485+ return ;
486+ }
487+
452488 for (unsigned ruleID : path)
453489 out << " (" << getRule (ruleID).getLHS () << " )" ;
454490}
@@ -483,6 +519,9 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
483519 (void ) simplify (baseTerm);
484520
485521 for (const auto &path : pair.second ) {
522+ if (path.empty ())
523+ continue ;
524+
486525 const auto &otherRule = getRule (path.back ());
487526 auto *otherProto = otherRule.getLHS ().back ().getProtocol ();
488527
@@ -568,6 +607,9 @@ static const ProtocolDecl *getParentConformanceForTerm(Term lhs) {
568607// / conformance rules.
569608void RewriteSystem::computeGeneratingConformances (
570609 llvm::DenseSet<unsigned > &redundantConformances) {
610+ // All conformance rules, sorted by left hand side.
611+ SmallVector<std::pair<unsigned , Term>, 4 > conformanceRules;
612+
571613 // Maps a conformance rule to a conformance path deriving the subject type's
572614 // base type. For example, consider the following conformance rule:
573615 //
@@ -586,27 +628,33 @@ void RewriteSystem::computeGeneratingConformances(
586628 // the form [P].[Q] => [P].
587629 llvm::DenseSet<unsigned > protocolRefinements;
588630
589- // Prepare the initial set of equations: every non-redundant conformance rule
590- // can be expressed as itself.
631+ // Prepare the initial set of equations.
591632 for (unsigned ruleID : indices (Rules)) {
592633 const auto &rule = getRule (ruleID);
634+ if (rule.isPermanent ())
635+ continue ;
636+
593637 if (rule.isRedundant ())
594638 continue ;
595639
596640 if (!rule.isProtocolConformanceRule ())
597641 continue ;
598642
643+ auto lhs = rule.getLHS ();
644+ conformanceRules.emplace_back (ruleID, lhs);
645+
646+ // Initially, every non-redundant conformance rule can be expressed
647+ // as itself.
599648 SmallVector<unsigned , 2 > path;
600649 path.push_back (ruleID);
601650 conformancePaths[ruleID].push_back (path);
602651
652+ // Save protocol refinement relations in a side table.
603653 if (rule.isProtocolRefinementRule ()) {
604654 protocolRefinements.insert (ruleID);
605655 continue ;
606656 }
607657
608- auto lhs = rule.getLHS ();
609-
610658 // Record a parent path if the subject type itself requires a non-trivial
611659 // conformance path to derive.
612660 if (auto *parentProto = getParentConformanceForTerm (lhs)) {
@@ -645,22 +693,33 @@ void RewriteSystem::computeGeneratingConformances(
645693
646694 verifyGeneratingConformanceEquations (conformancePaths);
647695
696+ // Sort the list of conformance rules in reverse order; we're going to try
697+ // to minimize away less canonical rules first.
698+ std::sort (conformanceRules.begin (), conformanceRules.end (),
699+ [&](const std::pair<unsigned , Term> &lhs,
700+ const std::pair<unsigned , Term> &rhs) -> bool {
701+ return lhs.second .compare (rhs.second , Context) > 0 ;
702+ });
703+
648704 // Find a minimal set of generating conformances.
649- for (const auto &pair : conformancePaths) {
650- bool isProtocolRefinement = protocolRefinements.count (pair.first ) > 0 ;
705+ for (const auto &pair : conformanceRules) {
706+ unsigned ruleID = pair.first ;
707+ const auto &paths = conformancePaths[ruleID];
651708
652- for (const auto &path : pair.second ) {
709+ bool isProtocolRefinement = protocolRefinements.count (ruleID) > 0 ;
710+
711+ for (const auto &path : paths) {
653712 // Only consider a protocol refinement rule to be redundant if it is
654713 // witnessed by a composition of other protocol refinement rules.
655714 if (isProtocolRefinement && !isValidRefinementPath (path))
656715 continue ;
657716
658717 llvm::SmallDenseSet<unsigned , 4 > visited;
659- visited.insert (pair. first );
718+ visited.insert (ruleID );
660719
661720 if (isValidConformancePath (visited, redundantConformances, path,
662721 parentPaths, conformancePaths)) {
663- redundantConformances.insert (pair. first );
722+ redundantConformances.insert (ruleID );
664723 break ;
665724 }
666725 }
@@ -700,4 +759,4 @@ void RewriteSystem::computeGeneratingConformances(
700759 llvm::dbgs () << " - " << getRule (pair.first ) << " \n " ;
701760 }
702761 }
703- }
762+ }
0 commit comments