30
30
// they are added by completion, or they are redundant rules written by the
31
31
// user.
32
32
//
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,
34
34
// decompositions can be found for all "derived" conformance rules, producing
35
35
// a minimal set of generating conformances.
36
36
//
69
69
using namespace swift ;
70
70
using namespace rewriting ;
71
71
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.
75
75
// /
76
76
// / The rules are organized by protocol. For each protocol, the first element
77
77
// / of the pair stores conformance rules that appear without context. The
78
78
// / second element of the pair stores rules that appear with non-empty left
79
79
// / context. For each such rule, the left prefix is also stored alongside.
80
80
void HomotopyGenerator::findProtocolConformanceRules (
81
81
llvm::SmallDenseMap<const ProtocolDecl *,
82
- std::pair<SmallVector<unsigned , 2 >,
83
- SmallVector<std::pair<MutableTerm, unsigned >, 2 >>>
84
- &result,
82
+ ProtocolConformanceRules, 2 > &result,
85
83
const RewriteSystem &system) const {
86
84
87
85
auto redundantRules = findRulesAppearingOnceInEmptyContext (system);
@@ -90,11 +88,13 @@ void HomotopyGenerator::findProtocolConformanceRules(
90
88
for (unsigned ruleID : redundantRules) {
91
89
const auto &rule = system.getRule (ruleID);
92
90
93
- if (rule.isIdentityConformanceRule ())
94
- continue ;
95
-
96
91
if (auto *proto = rule.isProtocolConformanceRule ()) {
97
- 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);
98
98
foundAny = true ;
99
99
}
100
100
}
@@ -105,7 +105,7 @@ void HomotopyGenerator::findProtocolConformanceRules(
105
105
RewritePathEvaluator evaluator (Basepoint);
106
106
107
107
// Now look for rewrite steps with conformance rules in empty right context,
108
- // 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])).
109
109
for (const auto &step : Path) {
110
110
if (!evaluator.isInContext ()) {
111
111
switch (step.Kind ) {
@@ -124,7 +124,7 @@ void HomotopyGenerator::findProtocolConformanceRules(
124
124
// the prefix term is 'X'.
125
125
const auto &term = evaluator.getCurrentTerm ();
126
126
MutableTerm prefix (term.begin (), term.begin () + step.StartOffset );
127
- result[proto].second .emplace_back (prefix, step.RuleID );
127
+ result[proto].RulesInContext .emplace_back (prefix, step.RuleID );
128
128
}
129
129
}
130
130
@@ -279,9 +279,7 @@ void RewriteSystem::computeCandidateConformancePaths(
279
279
continue ;
280
280
281
281
llvm::SmallDenseMap<const ProtocolDecl *,
282
- std::pair<SmallVector<unsigned , 2 >,
283
- SmallVector<std::pair<MutableTerm, unsigned >, 2 >>>
284
- result;
282
+ ProtocolConformanceRules, 2 > result;
285
283
286
284
loop.findProtocolConformanceRules (result, *this );
287
285
@@ -296,21 +294,18 @@ void RewriteSystem::computeCandidateConformancePaths(
296
294
297
295
for (const auto &pair : result) {
298
296
const auto *proto = pair.first ;
299
- const auto ¬InContext = pair.second .first ;
300
- 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 ;
301
300
302
301
// No rules appear without context.
303
- if (notInContext.empty ())
304
- continue ;
305
-
306
- // No replacement rules.
307
- if (notInContext.size () == 1 && inContext.empty ())
302
+ if (inEmptyContext.empty ())
308
303
continue ;
309
304
310
305
if (Debug.contains (DebugFlags::GeneratingConformances)) {
311
306
llvm::dbgs () << " * Protocol " << proto->getName () << " :\n " ;
312
307
llvm::dbgs () << " ** Conformance rules not in context:\n " ;
313
- for (unsigned ruleID : notInContext ) {
308
+ for (unsigned ruleID : inEmptyContext ) {
314
309
llvm::dbgs () << " -- (#" << ruleID << " ) " << getRule (ruleID) << " \n " ;
315
310
}
316
311
@@ -321,6 +316,10 @@ void RewriteSystem::computeCandidateConformancePaths(
321
316
llvm::dbgs () << " (#" << ruleID << " ) " << getRule (ruleID) << " \n " ;
322
317
}
323
318
319
+ if (sawIdentityConformance) {
320
+ llvm::dbgs () << " ** Equivalent to identity conformance\n " ;
321
+ }
322
+
324
323
llvm::dbgs () << " \n " ;
325
324
}
326
325
@@ -331,8 +330,8 @@ void RewriteSystem::computeCandidateConformancePaths(
331
330
//
332
331
// (T.[P] => T) := (T'.[P])
333
332
// (T'.[P] => T') := (T.[P])
334
- for (unsigned candidateRuleID : notInContext ) {
335
- for (unsigned otherRuleID : notInContext ) {
333
+ for (unsigned candidateRuleID : inEmptyContext ) {
334
+ for (unsigned otherRuleID : inEmptyContext ) {
336
335
if (otherRuleID == candidateRuleID)
337
336
continue ;
338
337
@@ -342,11 +341,22 @@ void RewriteSystem::computeCandidateConformancePaths(
342
341
}
343
342
}
344
343
345
- // Suppose a 3-cell contains a conformance rule (T.[P] => T) in an empty
346
- // 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
347
357
// context U.
348
358
//
349
- // The 3-cell looks something like this:
359
+ // The rewrite loop looks something like this:
350
360
//
351
361
// ... ⊗ (T.[P] => T) ⊗ ... ⊗ U.(V => V.[P]) ⊗ ...
352
362
// ^ ^
@@ -381,7 +391,7 @@ void RewriteSystem::computeCandidateConformancePaths(
381
391
382
392
// This decomposition defines a conformance access path for each
383
393
// conformance rule we saw in empty context.
384
- for (unsigned otherRuleID : notInContext )
394
+ for (unsigned otherRuleID : inEmptyContext )
385
395
conformancePaths[otherRuleID].push_back (conformancePath);
386
396
}
387
397
}
@@ -470,6 +480,11 @@ bool RewriteSystem::isValidRefinementPath(
470
480
void RewriteSystem::dumpConformancePath (
471
481
llvm::raw_ostream &out,
472
482
const SmallVectorImpl<unsigned > &path) const {
483
+ if (path.empty ()) {
484
+ out << " 1" ;
485
+ return ;
486
+ }
487
+
473
488
for (unsigned ruleID : path)
474
489
out << " (" << getRule (ruleID).getLHS () << " )" ;
475
490
}
@@ -504,6 +519,9 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
504
519
(void ) simplify (baseTerm);
505
520
506
521
for (const auto &path : pair.second ) {
522
+ if (path.empty ())
523
+ continue ;
524
+
507
525
const auto &otherRule = getRule (path.back ());
508
526
auto *otherProto = otherRule.getLHS ().back ().getProtocol ();
509
527
0 commit comments