@@ -610,46 +610,58 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {
610
610
// / emit warnings for in the source code.
611
611
void RewriteSystem::computeRedundantRequirementDiagnostics (
612
612
SmallVectorImpl<RequirementError> &errors) {
613
- // Map redundant rule IDs to their rewrite path for easy access
614
- // in the `isRedundantRule` lambda.
615
- llvm::SmallDenseMap<unsigned , RewritePath> redundantRules;
616
- for (auto &pair : RedundantRules)
617
- redundantRules[pair.first ] = pair.second ;
618
-
619
613
// Collect all rule IDs for each unique requirement ID.
620
- llvm::SmallDenseMap<unsigned , llvm::SmallDenseSet <unsigned , 2 >>
614
+ llvm::SmallDenseMap<unsigned , llvm::SmallVector <unsigned , 2 >>
621
615
rulesPerRequirement;
622
616
623
617
// Collect non-explicit requirements that are not redundant.
624
- llvm::SmallDenseSet<unsigned , 2 > impliedRequirements ;
618
+ llvm::SmallDenseSet<unsigned , 2 > nonExplicitNonRedundantRules ;
625
619
626
620
for (unsigned ruleID = FirstLocalRule, e = Rules.size ();
627
621
ruleID < e; ++ruleID) {
628
622
auto &rule = getRules ()[ruleID];
629
623
630
- if (!rule.getRequirementID ().hasValue () &&
631
- !rule.isPermanent () && !rule.isRedundant () &&
632
- isInMinimizationDomain (rule.getLHS ().getRootProtocol ()))
633
- impliedRequirements.insert (ruleID);
624
+ if (rule.isPermanent ())
625
+ continue ;
626
+
627
+ if (!isInMinimizationDomain (rule.getLHS ().getRootProtocol ()))
628
+ continue ;
634
629
635
630
auto requirementID = rule.getRequirementID ();
636
- if (!requirementID.hasValue ())
631
+
632
+ if (!requirementID.hasValue ()) {
633
+ if (!rule.isRedundant ())
634
+ nonExplicitNonRedundantRules.insert (ruleID);
635
+
637
636
continue ;
637
+ }
638
638
639
- rulesPerRequirement[*requirementID].insert (ruleID);
639
+ rulesPerRequirement[*requirementID].push_back (ruleID);
640
640
}
641
641
642
- auto isRedundantRule = [&](unsigned ruleID) {
643
- auto &rule = getRules ()[ruleID];
644
-
645
- // If this rule is replaced using a non-explicit,
646
- // non-redundant rule, it's not redundant.
647
- auto rewritePath = redundantRules[ruleID];
642
+ // Compute the set of redundant rules which transitively reference a
643
+ // non-explicit non-redundant rule. This updates nonExplicitNonRedundantRules.
644
+ //
645
+ // Since earlier redundant paths might reference rules which appear later in
646
+ // the list but not vice versa, walk the redundant paths in reverse order.
647
+ for (const auto &pair : llvm::reverse (RedundantRules)) {
648
+ // Pre-condition: the replacement path only references redundant rules
649
+ // which we have already seen. If any of those rules transitively reference
650
+ // a non-explicit, non-redundant rule, they have been inserted into the
651
+ // nonExplicitNonRedundantRules set on previous iterations.
652
+ unsigned ruleID = pair.first ;
653
+ const auto &rewritePath = pair.second ;
654
+
655
+ // Check if this rewrite path references a rule that is already known to
656
+ // either be non-explicit and non-redundant, or reference such a rule via
657
+ // it's redundancy path.
648
658
for (auto step : rewritePath) {
649
659
switch (step.Kind ) {
650
660
case RewriteStep::Rule: {
651
- if (impliedRequirements.count (step.getRuleID ()))
652
- return false ;
661
+ if (nonExplicitNonRedundantRules.count (step.getRuleID ())) {
662
+ nonExplicitNonRedundantRules.insert (ruleID);
663
+ continue ;
664
+ }
653
665
654
666
break ;
655
667
}
@@ -665,20 +677,37 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
665
677
}
666
678
}
667
679
668
- return rule.isRedundant ();
680
+ // Post-condition: If the current replacement path transitively references
681
+ // any non-explicit, non-redundant rules, then nonExplicitNonRedundantRules
682
+ // contains the current rule.
683
+ }
684
+
685
+ // We diagnose a redundancy if the rule is redundant, and if its replacement
686
+ // path does not transitively involve any non-explicit, non-redundant rules.
687
+ auto isRedundantRule = [&](unsigned ruleID) {
688
+ const auto &rule = getRules ()[ruleID];
689
+
690
+ return (rule.isRedundant () &&
691
+ nonExplicitNonRedundantRules.count (ruleID) == 0 );
669
692
};
670
693
694
+ // Finally walk through the written requirements, diagnosing any that are
695
+ // redundant.
671
696
for (auto requirementID : indices (WrittenRequirements)) {
672
697
auto requirement = WrittenRequirements[requirementID];
673
- auto pairIt = rulesPerRequirement.find (requirementID);
674
698
675
699
// Inferred requirements can be re-stated without warning.
676
700
if (requirement.inferred )
677
701
continue ;
678
702
679
- // If there are no rules for this structural requirement, then
680
- // the requirement was never added to the rewrite system because
681
- // it is trivially redundant.
703
+ auto pairIt = rulesPerRequirement.find (requirementID);
704
+
705
+ // If there are no rules for this structural requirement, then the
706
+ // requirement is unnecessary in the source code.
707
+ //
708
+ // This means the requirement was determined to be vacuous by
709
+ // requirement lowering and produced no rules, or the rewrite rules were
710
+ // trivially simplified by RewriteSystem::addRule().
682
711
if (pairIt == rulesPerRequirement.end ()) {
683
712
errors.push_back (
684
713
RequirementError::forRedundantRequirement (requirement.req ,
@@ -688,7 +717,7 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
688
717
689
718
// If all rules derived from this structural requirement are redundant,
690
719
// then the requirement is unnecessary in the source code.
691
- auto ruleIDs = pairIt->second ;
720
+ const auto & ruleIDs = pairIt->second ;
692
721
if (llvm::all_of (ruleIDs, isRedundantRule)) {
693
722
auto requirement = WrittenRequirements[requirementID];
694
723
errors.push_back (
0 commit comments