@@ -64,23 +64,38 @@ void HomotopyGenerator::findProtocolConformanceRules(
64
64
&result,
65
65
const RewriteSystem &system) const {
66
66
67
+ auto redundantRules = Path.findRulesAppearingOnceInEmptyContext ();
68
+
69
+ bool foundAny = false ;
70
+ for (unsigned ruleID : redundantRules) {
71
+ const auto &rule = system.getRule (ruleID);
72
+ if (auto *proto = rule.isProtocolConformanceRule ()) {
73
+ result[proto].first .push_back (ruleID);
74
+ foundAny = true ;
75
+ }
76
+ }
77
+
78
+ if (!foundAny)
79
+ return ;
80
+
67
81
MutableTerm term = Basepoint;
68
82
83
+ // Now look for rewrite steps with conformance rules in empty right context,
84
+ // that is something like X.(Y.[P] => Z) (or it's inverse, X.(Z => Y.[P])).
69
85
for (const auto &step : Path) {
70
86
switch (step.Kind ) {
71
87
case RewriteStep::ApplyRewriteRule: {
72
88
const auto &rule = system.getRule (step.RuleID );
73
- if (!rule.isProtocolConformanceRule ())
74
- break ;
75
-
76
- auto *proto = rule.getLHS ().back ().getProtocol ();
77
-
78
- if (!step.isInContext ()) {
79
- result[proto].first .push_back (step.RuleID );
80
- } else if (step.StartOffset > 0 &&
81
- step.EndOffset == 0 ) {
82
- MutableTerm prefix (term.begin (), term.begin () + step.StartOffset );
83
- result[proto].second .emplace_back (prefix, step.RuleID );
89
+ if (auto *proto = rule.isProtocolConformanceRule ()) {
90
+ if (step.StartOffset > 0 &&
91
+ step.EndOffset == 0 ) {
92
+ // Record the prefix term that is left unchanged by this rewrite step.
93
+ //
94
+ // In the above example where the rewrite step is X.(Y.[P] => Z),
95
+ // the prefix term is 'X'.
96
+ MutableTerm prefix (term.begin (), term.begin () + step.StartOffset );
97
+ result[proto].second .emplace_back (prefix, step.RuleID );
98
+ }
84
99
}
85
100
86
101
break ;
@@ -335,40 +350,80 @@ bool RewriteSystem::isValidConformancePath(
335
350
llvm::SmallDenseSet<unsigned , 4 > &visited,
336
351
llvm::DenseSet<unsigned > &redundantConformances,
337
352
const llvm::SmallVectorImpl<unsigned > &path,
353
+ const llvm::MapVector<unsigned , SmallVector<unsigned , 2 >> &parentPaths,
338
354
const llvm::MapVector<unsigned ,
339
355
std::vector<SmallVector<unsigned , 2 >>>
340
356
&conformancePaths) const {
341
357
for (unsigned ruleID : path) {
342
358
if (visited.count (ruleID) > 0 )
343
359
return false ;
344
360
345
- if (!redundantConformances.count (ruleID))
346
- continue ;
347
-
348
- SWIFT_DEFER {
349
- visited.erase (ruleID);
350
- };
351
- visited.insert (ruleID);
361
+ if (redundantConformances.count (ruleID)) {
362
+ SWIFT_DEFER {
363
+ visited.erase (ruleID);
364
+ };
365
+ visited.insert (ruleID);
366
+
367
+ auto found = conformancePaths.find (ruleID);
368
+ assert (found != conformancePaths.end ());
369
+
370
+ bool foundValidConformancePath = false ;
371
+ for (const auto &otherPath : found->second ) {
372
+ if (isValidConformancePath (visited, redundantConformances, otherPath,
373
+ parentPaths, conformancePaths)) {
374
+ foundValidConformancePath = true ;
375
+ break ;
376
+ }
377
+ }
352
378
353
- auto found = conformancePaths.find (ruleID);
354
- assert (found != conformancePaths.end ());
379
+ if (!foundValidConformancePath)
380
+ return false ;
381
+ }
355
382
356
- bool foundValidConformancePath = false ;
357
- for (const auto &otherPath : found->second ) {
358
- if (isValidConformancePath (visited, redundantConformances,
359
- otherPath, conformancePaths)) {
360
- foundValidConformancePath = true ;
361
- break ;
383
+ auto found = parentPaths.find (ruleID);
384
+ if (found != parentPaths.end ()) {
385
+ SWIFT_DEFER {
386
+ visited.erase (ruleID);
387
+ };
388
+ visited.insert (ruleID);
389
+
390
+ // If 'req' is based on some other conformance requirement
391
+ // `T.[P.]A : Q', we want to make sure that we have a
392
+ // non-redundant derivation for 'T : P'.
393
+ if (!isValidConformancePath (visited, redundantConformances, found->second ,
394
+ parentPaths, conformancePaths)) {
395
+ return false ;
362
396
}
363
397
}
398
+ }
364
399
365
- if (!foundValidConformancePath)
400
+ return true ;
401
+ }
402
+
403
+ // / Rules of the form [P].[Q] => [P] encode protocol refinement and can only
404
+ // / be redundant if they're equivalent to a sequence of other protocol
405
+ // / refinements.
406
+ // /
407
+ // / This helps ensure that the inheritance clause of a protocol is complete
408
+ // / and correct, allowing name lookup to find associated types of inherited
409
+ // / protocols while building the protocol requirement signature.
410
+ bool RewriteSystem::isValidRefinementPath (
411
+ const llvm::SmallVectorImpl<unsigned > &path) const {
412
+ for (unsigned ruleID : path) {
413
+ if (!getRule (ruleID).isProtocolRefinementRule ())
366
414
return false ;
367
415
}
368
416
369
417
return true ;
370
418
}
371
419
420
+ void RewriteSystem::dumpConformancePath (
421
+ llvm::raw_ostream &out,
422
+ const SmallVectorImpl<unsigned > &path) const {
423
+ for (unsigned ruleID : path)
424
+ out << " (" << getRule (ruleID).getLHS () << " )" ;
425
+ }
426
+
372
427
void RewriteSystem::dumpGeneratingConformanceEquation (
373
428
llvm::raw_ostream &out,
374
429
unsigned baseRuleID,
@@ -381,8 +436,8 @@ void RewriteSystem::dumpGeneratingConformanceEquation(
381
436
out << " ∨ " ;
382
437
else
383
438
first = false ;
384
- for ( unsigned ruleID : path)
385
- out << " ( " << getRule (ruleID). getLHS () << " ) " ;
439
+
440
+ dumpConformancePath ( out, path) ;
386
441
}
387
442
}
388
443
@@ -442,8 +497,24 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
442
497
// / conformance rules.
443
498
void RewriteSystem::computeGeneratingConformances (
444
499
llvm::DenseSet<unsigned > &redundantConformances) {
500
+ // Maps a conformance rule to a conformance path deriving the subject type's
501
+ // base type. For example, consider the following conformance rule:
502
+ //
503
+ // T.[P:A].[Q:B].[R] => T.[P:A].[Q:B]
504
+ //
505
+ // The subject type is T.[P:A].[Q:B]; in order to derive the metadata, we need
506
+ // the witness table for T.[P:A] : [Q] first, by computing a conformance access
507
+ // path for the term T.[P:A].[Q], known as the 'parent path'.
508
+ llvm::MapVector<unsigned , SmallVector<unsigned , 2 >> parentPaths;
509
+
510
+ // Maps a conformance rule to a list of paths. Each path in the list is a unique
511
+ // derivation of the conformance in terms of other conformance rules.
445
512
llvm::MapVector<unsigned , std::vector<SmallVector<unsigned , 2 >>> conformancePaths;
446
513
514
+ // The set of conformance rules which are protocol refinements, that is rules of
515
+ // the form [P].[Q] => [P].
516
+ llvm::DenseSet<unsigned > protocolRefinements;
517
+
447
518
// Prepare the initial set of equations: every non-redundant conformance rule
448
519
// can be expressed as itself.
449
520
for (unsigned ruleID : indices (Rules)) {
@@ -457,6 +528,57 @@ void RewriteSystem::computeGeneratingConformances(
457
528
SmallVector<unsigned , 2 > path;
458
529
path.push_back (ruleID);
459
530
conformancePaths[ruleID].push_back (path);
531
+
532
+ if (rule.isProtocolRefinementRule ()) {
533
+ protocolRefinements.insert (ruleID);
534
+ continue ;
535
+ }
536
+
537
+ auto lhs = rule.getLHS ();
538
+
539
+ auto parentSymbol = lhs[lhs.size () - 2 ];
540
+
541
+ // The last element is a protocol symbol, because this is a conformance rule.
542
+ // The second to last symbol is either an associated type, protocol or generic
543
+ // parameter symbol.
544
+ switch (parentSymbol.getKind ()) {
545
+ case Symbol::Kind::AssociatedType: {
546
+ // If we have a rule of the form X.[P:Y].[Q] => X.[P:Y] wih non-empty X,
547
+ // then the parent type is X.[P].
548
+ if (lhs.size () == 2 )
549
+ continue ;
550
+
551
+ MutableTerm mutTerm (lhs.begin (), lhs.end () - 2 );
552
+ assert (!mutTerm.empty ());
553
+
554
+ const auto protos = parentSymbol.getProtocols ();
555
+ assert (protos.size () == 1 );
556
+
557
+ bool simplified = simplify (mutTerm);
558
+ assert (!simplified || rule.isSimplified ());
559
+ (void ) simplified;
560
+
561
+ mutTerm.add (Symbol::forProtocol (protos[0 ], Context));
562
+
563
+ // Get a conformance path for X.[P] and record it.
564
+ decomposeTermIntoConformanceRuleLeftHandSides (mutTerm, parentPaths[ruleID]);
565
+ continue ;
566
+ }
567
+
568
+ case Symbol::Kind::GenericParam:
569
+ case Symbol::Kind::Protocol:
570
+ // Don't record a parent path, since the parent type is trivial (either a
571
+ // generic parameter, or the protocol 'Self' type).
572
+ continue ;
573
+
574
+ case Symbol::Kind::Name:
575
+ case Symbol::Kind::Layout:
576
+ case Symbol::Kind::Superclass:
577
+ case Symbol::Kind::ConcreteType:
578
+ break ;
579
+ }
580
+
581
+ llvm_unreachable (" Bad symbol kind" );
460
582
}
461
583
462
584
computeCandidateConformancePaths (conformancePaths);
@@ -469,18 +591,32 @@ void RewriteSystem::computeGeneratingConformances(
469
591
pair.first , pair.second );
470
592
llvm::dbgs () << " \n " ;
471
593
}
594
+
595
+ llvm::dbgs () << " Parent paths:\n " ;
596
+ for (const auto &pair : parentPaths) {
597
+ llvm::dbgs () << " - " << getRule (pair.first ).getLHS () << " : " ;
598
+ dumpConformancePath (llvm::dbgs (), pair.second );
599
+ llvm::dbgs () << " \n " ;
600
+ }
472
601
}
473
602
474
603
verifyGeneratingConformanceEquations (conformancePaths);
475
604
476
605
// Find a minimal set of generating conformances.
477
606
for (const auto &pair : conformancePaths) {
607
+ bool isProtocolRefinement = protocolRefinements.count (pair.first ) > 0 ;
608
+
478
609
for (const auto &path : pair.second ) {
610
+ // Only consider a protocol refinement rule to be redundant if it is
611
+ // witnessed by a composition of other protocol refinement rules.
612
+ if (isProtocolRefinement && !isValidRefinementPath (path))
613
+ continue ;
614
+
479
615
llvm::SmallDenseSet<unsigned , 4 > visited;
480
616
visited.insert (pair.first );
481
617
482
- if (isValidConformancePath (visited, redundantConformances,
483
- path , conformancePaths)) {
618
+ if (isValidConformancePath (visited, redundantConformances, path,
619
+ parentPaths , conformancePaths)) {
484
620
redundantConformances.insert (pair.first );
485
621
break ;
486
622
}
@@ -502,7 +638,7 @@ void RewriteSystem::computeGeneratingConformances(
502
638
abort ();
503
639
}
504
640
505
- if (rule.containsUnresolvedSymbols ()) {
641
+ if (rule.getLHS (). containsUnresolvedSymbols ()) {
506
642
llvm::errs () << " Generating conformance contains unresolved symbols: " ;
507
643
llvm::errs () << rule << " \n\n " ;
508
644
dump (llvm::errs ());
0 commit comments