@@ -610,46 +610,58 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {
610610// / emit warnings for in the source code.
611611void RewriteSystem::computeRedundantRequirementDiagnostics (
612612 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-
619613 // 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 >>
621615 rulesPerRequirement;
622616
623617 // Collect non-explicit requirements that are not redundant.
624- llvm::SmallDenseSet<unsigned , 2 > impliedRequirements ;
618+ llvm::SmallDenseSet<unsigned , 2 > nonExplicitNonRedundantRules ;
625619
626620 for (unsigned ruleID = FirstLocalRule, e = Rules.size ();
627621 ruleID < e; ++ruleID) {
628622 auto &rule = getRules ()[ruleID];
629623
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 ;
634629
635630 auto requirementID = rule.getRequirementID ();
636- if (!requirementID.hasValue ())
631+
632+ if (!requirementID.hasValue ()) {
633+ if (!rule.isRedundant ())
634+ nonExplicitNonRedundantRules.insert (ruleID);
635+
637636 continue ;
637+ }
638638
639- rulesPerRequirement[*requirementID].insert (ruleID);
639+ rulesPerRequirement[*requirementID].push_back (ruleID);
640640 }
641641
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.
648658 for (auto step : rewritePath) {
649659 switch (step.Kind ) {
650660 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+ }
653665
654666 break ;
655667 }
@@ -665,20 +677,37 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
665677 }
666678 }
667679
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 );
669692 };
670693
694+ // Finally walk through the written requirements, diagnosing any that are
695+ // redundant.
671696 for (auto requirementID : indices (WrittenRequirements)) {
672697 auto requirement = WrittenRequirements[requirementID];
673- auto pairIt = rulesPerRequirement.find (requirementID);
674698
675699 // Inferred requirements can be re-stated without warning.
676700 if (requirement.inferred )
677701 continue ;
678702
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().
682711 if (pairIt == rulesPerRequirement.end ()) {
683712 errors.push_back (
684713 RequirementError::forRedundantRequirement (requirement.req ,
@@ -688,7 +717,7 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
688717
689718 // If all rules derived from this structural requirement are redundant,
690719 // then the requirement is unnecessary in the source code.
691- auto ruleIDs = pairIt->second ;
720+ const auto & ruleIDs = pairIt->second ;
692721 if (llvm::all_of (ruleIDs, isRedundantRule)) {
693722 auto requirement = WrittenRequirements[requirementID];
694723 errors.push_back (
0 commit comments